@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.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,9 +1056,6 @@ function getVariantKey(variant) {
983
1056
  }
984
1057
 
985
1058
  // src/core/wallet-actions.ts
986
- var TransferMode = {
987
- native: "native"
988
- };
989
1059
  var WalletExecutionKind = {
990
1060
  signMessage: "sign-message"
991
1061
  };
@@ -1016,8 +1086,7 @@ function executeWith(options) {
1016
1086
  chain: options.chain,
1017
1087
  message: action.message,
1018
1088
  account: options.account ?? action.account,
1019
- actionType: action.actionType,
1020
- transferMode: action.transferMode
1089
+ actionType: action.actionType
1021
1090
  });
1022
1091
  return action.submit({
1023
1092
  signature,
@@ -1240,7 +1309,6 @@ var AccountsModule = class {
1240
1309
  kind: WalletActionKind.createAccount,
1241
1310
  executionKind: WalletExecutionKind.signMessage,
1242
1311
  actionType: WalletActionKind.createAccount,
1243
- transferMode: TransferMode.native,
1244
1312
  account: normalizedAccount,
1245
1313
  message: createInitializeAccountMessage(expiryTimestamp, nonce),
1246
1314
  data: {
@@ -1331,8 +1399,8 @@ function mapCanisterWalletToWallet(canisterWallet) {
1331
1399
  );
1332
1400
  }
1333
1401
  }
1334
- var KNOWN_ASSET_TAGS = ["BTC", "USDC", "USDT"];
1335
- var KNOWN_CHAIN_TAGS = ["BTC", "ETH"];
1402
+ var KNOWN_ASSET_TAGS = ["BTC", "ICP", "USDC", "USDT"];
1403
+ var KNOWN_CHAIN_TAGS = ["BTC", "ETH", "ICP"];
1336
1404
  function extractVariantTag(variant, knownTags) {
1337
1405
  const [key] = Object.keys(variant);
1338
1406
  if (!key) {
@@ -1911,17 +1979,14 @@ function mapInstantLoanLookupError(error) {
1911
1979
  }
1912
1980
  function mapActivity(wire) {
1913
1981
  const amount = parseBigInt(wire.amount, "activity amount");
1914
- const activityBase = {
1915
- id: wire.id,
1916
- poolId: wire.poolId,
1917
- asset: wire.asset,
1918
- chain: wire.chain,
1919
- amount,
1920
- timestampMs: wire.timestampMs
1921
- };
1922
1982
  if (isInflowOperation(wire.status.operation)) {
1923
1983
  const activity = {
1924
- ...activityBase,
1984
+ id: wire.id,
1985
+ poolId: wire.poolId,
1986
+ asset: wire.asset,
1987
+ chain: wire.chain,
1988
+ amount,
1989
+ timestampMs: wire.timestampMs,
1925
1990
  status: wire.status
1926
1991
  };
1927
1992
  if (wire.txids) {
@@ -1934,7 +1999,12 @@ function mapActivity(wire) {
1934
1999
  }
1935
2000
  if (isOutflowOperation(wire.status.operation)) {
1936
2001
  const activity = {
1937
- ...activityBase,
2002
+ id: wire.id,
2003
+ poolId: wire.poolId,
2004
+ asset: wire.asset,
2005
+ chain: wire.chain,
2006
+ amount,
2007
+ timestampMs: wire.timestampMs,
1938
2008
  status: wire.status
1939
2009
  };
1940
2010
  if (wire.txids) {
@@ -2110,6 +2180,72 @@ function validateHistoryStateFilter(state) {
2110
2180
  );
2111
2181
  }
2112
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
+ }
2113
2249
 
2114
2250
  // src/core/status.ts
2115
2251
  function createLiquidiumStatus(params) {
@@ -2159,6 +2295,7 @@ function throwInvalidApiResponseField(field) {
2159
2295
  // src/core/utils/asset-decimals.ts
2160
2296
  var ASSET_NATIVE_DECIMALS = {
2161
2297
  BTC: 8n,
2298
+ ICP: 8n,
2162
2299
  USDC: 6n,
2163
2300
  USDT: 6n,
2164
2301
  SOL: 9n
@@ -2554,7 +2691,7 @@ var idlFactory2 = ({ IDL }) => {
2554
2691
 
2555
2692
  // src/core/canisters/ckbtc/minter.ts
2556
2693
  function createCkBtcMinterActor(canisterContext) {
2557
- const canisterId = CK_CANISTER_IDS.btcMinter;
2694
+ const canisterId = CK_CANISTER_IDS.BTC.minter;
2558
2695
  return agent.Actor.createActor(idlFactory2, {
2559
2696
  agent: canisterContext.agent,
2560
2697
  canisterId
@@ -3005,55 +3142,43 @@ var EvmSupplyApprovalStrategy = {
3005
3142
  };
3006
3143
 
3007
3144
  // src/modules/lending/_internal/supply-targets.ts
3008
- async function resolveSupplyTarget(canisterContext, request) {
3009
- const selectedPool = await getPoolById(canisterContext, request.poolId);
3010
- const asset = selectedPool.asset;
3011
- const chain = selectedPool.chain;
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
+ });
3012
3151
  const mechanism = resolveSupplyMechanism({
3013
- asset,
3014
- chain,
3015
- requestedMechanism: request.mechanism
3152
+ identifier,
3153
+ mechanism: request.mechanism
3016
3154
  });
3017
- switch (mechanism) {
3018
- case SupplyPlanType.transfer:
3019
- return await getNativeAddressSupplyTarget(
3020
- canisterContext,
3021
- request.profileId,
3022
- {
3023
- poolId: request.poolId,
3024
- asset,
3025
- chain,
3026
- action: request.action
3027
- }
3028
- );
3029
- case SupplyPlanType.contractInteraction:
3030
- return getIcrcAccountSupplyTarget(request.profileId, {
3031
- poolId: request.poolId,
3032
- asset,
3033
- chain,
3034
- action: request.action
3035
- });
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);
3036
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
+ );
3037
3171
  }
3038
3172
  function resolveSupplyMechanism(params) {
3039
- if (params.asset === Asset.BTC && params.chain === Chain.BTC) {
3040
- if (params.requestedMechanism === SupplyPlanType.contractInteraction) {
3041
- throw new LiquidiumError(
3042
- LiquidiumErrorCode.VALIDATION_ERROR,
3043
- "Contract-interaction supply is not supported for BTC on BTC"
3044
- );
3045
- }
3173
+ if (params.mechanism !== SupplyPlanType.contractInteraction) {
3046
3174
  return SupplyPlanType.transfer;
3047
3175
  }
3048
- if (isEthStablecoin(params.asset, params.chain)) {
3049
- if (params.requestedMechanism) {
3050
- return params.requestedMechanism;
3051
- }
3052
- return SupplyPlanType.transfer;
3176
+ if (params.identifier.chain === Chain.ETH && (params.identifier.asset === Asset.USDC || params.identifier.asset === Asset.USDT)) {
3177
+ return SupplyPlanType.contractInteraction;
3053
3178
  }
3054
3179
  throw new LiquidiumError(
3055
3180
  LiquidiumErrorCode.VALIDATION_ERROR,
3056
- `No supply mechanism is configured for ${params.asset} on ${params.chain}`
3181
+ `Contract-interaction supply is not supported for ${params.identifier.asset} on ${params.identifier.chain}`
3057
3182
  );
3058
3183
  }
3059
3184
  function isEthStablecoin(asset, chain) {
@@ -3125,9 +3250,33 @@ async function getPoolById(canisterContext, poolId) {
3125
3250
  }
3126
3251
  return decodedPool;
3127
3252
  }
3128
- async function getNativeAddressSupplyTarget(canisterContext, profileId, request) {
3129
- assertSupportsNativeAddressInflowTarget(request.asset, request.chain);
3130
- if (isEthStablecoin(request.asset, request.chain)) {
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) {
3131
3280
  const tokenAddress = getEthStablecoinContractAddress(request.asset);
3132
3281
  const subaccount2 = encodeInflowSubaccount({
3133
3282
  action: request.action,
@@ -3146,7 +3295,6 @@ async function getNativeAddressSupplyTarget(canisterContext, profileId, request)
3146
3295
  throw mapDepositAccountErrorToLiquidiumError(result.Err);
3147
3296
  }
3148
3297
  return {
3149
- type: "nativeAddress",
3150
3298
  poolId: request.poolId,
3151
3299
  asset: request.asset,
3152
3300
  chain: request.chain,
@@ -3154,11 +3302,11 @@ async function getNativeAddressSupplyTarget(canisterContext, profileId, request)
3154
3302
  address: result.Ok
3155
3303
  };
3156
3304
  }
3157
- const configuredBtcPoolId = canisterContext.canisterIds.btcPool;
3305
+ const configuredBtcPoolId = canisterContext.canisterIds.pools.btc;
3158
3306
  if (request.poolId !== configuredBtcPoolId) {
3159
3307
  throw new LiquidiumError(
3160
3308
  LiquidiumErrorCode.VALIDATION_ERROR,
3161
- `Native BTC inflow targets require the configured BTC pool ${configuredBtcPoolId}, received ${request.poolId}`
3309
+ `Chain-address BTC inflow targets require the configured BTC pool ${configuredBtcPoolId}, received ${request.poolId}`
3162
3310
  );
3163
3311
  }
3164
3312
  const subaccount = encodeInflowSubaccount({
@@ -3171,56 +3319,42 @@ async function getNativeAddressSupplyTarget(canisterContext, profileId, request)
3171
3319
  subaccount: [subaccount]
3172
3320
  }
3173
3321
  );
3174
- return {
3175
- type: "nativeAddress",
3176
- poolId: request.poolId,
3177
- asset: request.asset,
3178
- chain: request.chain,
3179
- action: request.action,
3180
- address
3181
- };
3322
+ return { ...request, address };
3182
3323
  }
3183
3324
  function getIcrcAccountSupplyTarget(profileId, request) {
3184
- assertSupportsIcrcAccountInflowTarget(request.asset);
3325
+ const account = createInflowIcrcAccount(profileId, request);
3326
+ return { ...request, address: account.address };
3327
+ }
3328
+ function getIcpLedgerSupplyTarget(profileId, request) {
3185
3329
  const poolPrincipal = principal.Principal.fromText(request.poolId);
3186
3330
  const subaccount = encodeInflowSubaccount({
3187
3331
  action: request.action,
3188
3332
  principal: principal.Principal.fromText(profileId)
3189
3333
  });
3334
+ const account = createIcrcAccount({
3335
+ owner: poolPrincipal,
3336
+ subaccount
3337
+ });
3190
3338
  return {
3191
- type: "icrcAccount",
3192
3339
  poolId: request.poolId,
3193
- asset: request.asset,
3194
- chain: request.chain,
3340
+ asset: Asset.ICP,
3341
+ chain: Chain.ICP,
3195
3342
  action: request.action,
3196
- owner: poolPrincipal.toText(),
3197
- subaccount,
3198
- account: icrc.encodeIcrcAccount({
3343
+ address: account.address,
3344
+ icpAccountIdentifier: encodeIcpAccountIdentifier({
3199
3345
  owner: poolPrincipal,
3200
3346
  subaccount
3201
3347
  })
3202
3348
  };
3203
3349
  }
3204
- function assertSupportsNativeAddressInflowTarget(asset, chain) {
3205
- if (asset === Asset.BTC && chain === Chain.BTC) {
3206
- return;
3207
- }
3208
- if (isEthStablecoin(asset, chain)) {
3209
- return;
3210
- }
3211
- throw new LiquidiumError(
3212
- LiquidiumErrorCode.VALIDATION_ERROR,
3213
- `Native address inflow targets are not supported for ${asset} on ${chain}`
3214
- );
3215
- }
3216
- function assertSupportsIcrcAccountInflowTarget(asset) {
3217
- if (asset === Asset.BTC || asset === Asset.USDT || asset === Asset.USDC) {
3218
- return;
3219
- }
3220
- throw new LiquidiumError(
3221
- LiquidiumErrorCode.VALIDATION_ERROR,
3222
- `ICRC account inflow targets are not supported for ${asset}`
3223
- );
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
+ });
3224
3358
  }
3225
3359
 
3226
3360
  // src/core/borrow-minimums.ts
@@ -3694,25 +3828,20 @@ function isEthStablecoin2(asset) {
3694
3828
  }
3695
3829
 
3696
3830
  // src/modules/instant-loans/_internal/address-validation.ts
3697
- function validateInstantLoanBorrowDestination(address, asset) {
3831
+ function validateInstantLoanBorrowDestination(address, asset, chain) {
3698
3832
  return normalizeExternalAddress({
3699
3833
  address,
3700
3834
  asset,
3701
- chain: getChainForInstantLoanAsset(asset)
3835
+ chain
3702
3836
  });
3703
3837
  }
3704
- function validateInstantLoanRefundDestination(address, asset) {
3838
+ function validateInstantLoanRefundDestination(address, asset, chain) {
3705
3839
  return normalizeExternalAddress({
3706
3840
  address,
3707
3841
  asset,
3708
- chain: getChainForInstantLoanAsset(asset)
3842
+ chain
3709
3843
  });
3710
3844
  }
3711
- function getChainForInstantLoanAsset(asset) {
3712
- if (asset === Asset.BTC) return Chain.BTC;
3713
- if (asset === Asset.USDC || asset === Asset.USDT) return Chain.ETH;
3714
- return asset;
3715
- }
3716
3845
 
3717
3846
  // src/modules/instant-loans/instant-loans.ts
3718
3847
  var REPAYMENT_BUFFER_SECONDS = 86400n;
@@ -3725,10 +3854,32 @@ var INSTANT_LOAN_FIND_QUERY_MAX_LENGTH = 256;
3725
3854
  var INSTANT_LOAN_WIRE_CONTEXT = "instant loan";
3726
3855
  var INSTANT_LOAN_ASSETS = [
3727
3856
  Asset.BTC,
3728
- Asset.SOL,
3857
+ Asset.ICP,
3729
3858
  Asset.USDC,
3730
3859
  Asset.USDT
3731
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
+ };
3732
3883
  var InstantLoansModule = class {
3733
3884
  constructor(canisterContext, apiClient, activities, lending, positions) {
3734
3885
  this.canisterContext = canisterContext;
@@ -3751,37 +3902,39 @@ var InstantLoansModule = class {
3751
3902
  * selected pool decimals, and call `client.quote.calculateLtv(...)` before
3752
3903
  * creation to block invalid LTV input.
3753
3904
  *
3754
- * `borrowDestination` receives the borrowed asset after the loan starts.
3755
- * `refundDestination` receives collateral refunds or withdrawals. Use
3905
+ * `borrow.destination` receives the borrowed asset after the loan starts.
3906
+ * `refund.destination` receives collateral refunds or withdrawals. Use
3756
3907
  * `depositWindowSeconds` for the user-facing collateral deposit timeout; the
3757
3908
  * SDK maps it to the canister's internal `ltv_timer_s` field.
3758
3909
  *
3759
- * @param request - Pool ids, assets, base-unit amounts, LTV limit, timeout, and destinations.
3910
+ * @param request - Collateral, borrow, refund, LTV limit, timeout, and inflow options.
3760
3911
  * @returns Hydrated loan state plus generated initial-deposit and repayment quote targets.
3761
3912
  */
3762
3913
  async create(request) {
3763
3914
  validateCreateRequest(request);
3764
- const borrowDestination = {
3765
- External: validateInstantLoanBorrowDestination(
3766
- addressFromAccountInput(request.borrowDestination),
3767
- request.borrowAsset
3768
- )
3769
- };
3770
- const refundDestination = {
3771
- External: validateInstantLoanRefundDestination(
3772
- addressFromAccountInput(request.refundDestination),
3773
- request.collateralAsset
3774
- )
3775
- };
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
+ });
3776
3929
  const apiClient = this.requireApi("Instant loan creation");
3777
3930
  await this.validateInstantLoanLtvPolicy(request);
3778
3931
  const response = await apiClient.post(SdkApiPath.instantLoans, {
3779
- collateralPoolId: request.collateralPoolId,
3780
- borrowPoolId: request.borrowPoolId,
3781
- collateralAsset: request.collateralAsset,
3782
- borrowAsset: request.borrowAsset,
3783
- collateralAmount: request.collateralAmount.toString(),
3784
- borrowAmount: request.borrowAmount.toString(),
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(),
3785
3938
  ltvMaxBps: request.ltvMaxBps.toString(),
3786
3939
  depositWindowSeconds: request.depositWindowSeconds.toString(),
3787
3940
  borrowDestination,
@@ -3791,15 +3944,21 @@ var InstantLoansModule = class {
3791
3944
  context: INSTANT_LOAN_WIRE_CONTEXT,
3792
3945
  label: "loan ID"
3793
3946
  });
3794
- const collateralAmount = parseUnsignedApiBigint(
3795
- response.loan.collateral.amountHint,
3796
- {
3797
- context: INSTANT_LOAN_WIRE_CONTEXT,
3798
- label: "collateral amount"
3799
- }
3800
- );
3801
- const record = await this.getLoanRecord(loanId);
3802
- return await this.mapLoanRecord(record, collateralAmount);
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
+ }
3803
3962
  }
3804
3963
  /**
3805
3964
  * Resolves canonical canister state by loan id or short reference.
@@ -3965,7 +4124,16 @@ var InstantLoansModule = class {
3965
4124
  throw mapCanisterCallErrorToLiquidiumError("get_loan", error);
3966
4125
  }
3967
4126
  }
3968
- 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
+ );
3969
4137
  return await this.hydrateLoan({
3970
4138
  loanId: record.id,
3971
4139
  profileId: record.lending_profile.toText(),
@@ -3974,17 +4142,18 @@ var InstantLoansModule = class {
3974
4142
  collateralPoolId: record.lend_pool_id.toText(),
3975
4143
  collateralAmount,
3976
4144
  borrowPoolId: record.borrow_pool_id.toText(),
3977
- collateralAsset: record.lend_asset,
3978
- borrowAsset: record.borrow_asset,
4145
+ collateralAsset,
4146
+ borrowAsset,
3979
4147
  borrowAmount: record.borrow_amount,
3980
- borrowDestination: accountFromCanister(record.borrow_destination),
4148
+ borrowDestination,
3981
4149
  refundDestination: accountFromCanister(record.refund_destination),
3982
4150
  started: record.started,
3983
4151
  depositDetectedTimestamp: record.deposit_detected_ts[0] ?? null,
3984
4152
  expiryTimestamp: record.expires_at[0] ?? deriveDepositExpiryTimestamp({
3985
4153
  depositDetectedTimestamp: record.deposit_detected_ts[0] ?? null,
3986
4154
  depositWindowSeconds: record.ltv_timer_s
3987
- })
4155
+ }),
4156
+ borrowChain: chainOptions.borrowChain ?? inferInstantLoanDeliveryChain(borrowDestination, borrowAsset)
3988
4157
  });
3989
4158
  }
3990
4159
  async getCollateralAmountHint(loanId) {
@@ -4004,24 +4173,22 @@ var InstantLoansModule = class {
4004
4173
  const collateralAsset = input.collateralAsset;
4005
4174
  const borrowAsset = input.borrowAsset;
4006
4175
  const [
4007
- depositTarget,
4008
- repayTarget,
4176
+ depositTargetSet,
4177
+ repaymentTargetSet,
4009
4178
  collateralPosition,
4010
4179
  borrowPosition,
4011
4180
  borrowPoolRate,
4012
4181
  activeActivities
4013
4182
  ] = await Promise.all([
4014
- resolveSupplyTarget(this.canisterContext, {
4183
+ this.resolveInstantLoanInflowTargets({
4015
4184
  profileId,
4016
4185
  poolId: collateralPoolId,
4017
- action: SupplyAction.deposit,
4018
- mechanism: SupplyPlanType.transfer
4186
+ action: SupplyAction.deposit
4019
4187
  }),
4020
- resolveSupplyTarget(this.canisterContext, {
4188
+ this.resolveInstantLoanInflowTargets({
4021
4189
  profileId,
4022
4190
  poolId: borrowPoolId,
4023
- action: SupplyAction.repayment,
4024
- mechanism: SupplyPlanType.transfer
4191
+ action: SupplyAction.repayment
4025
4192
  }),
4026
4193
  this.positions.getPosition(profileId, collateralPoolId),
4027
4194
  this.positions.getPosition(profileId, borrowPoolId),
@@ -4033,8 +4200,6 @@ var InstantLoansModule = class {
4033
4200
  borrowPosition,
4034
4201
  borrowPoolRate.borrowRate
4035
4202
  );
4036
- const repaymentInflowFee = totalDebtAmount > 0n ? await this.estimateRepaymentInflowFee(borrowAsset, repayTarget.chain) : { totalFee: 0n, estimateAvailable: false };
4037
- const repaymentAmount = totalDebtAmount + interestBufferAmount + repaymentInflowFee.totalFee;
4038
4203
  const currentCollateralAmount = collateralPosition?.deposited ?? 0n;
4039
4204
  const collateralAmount = input.collateralAmount;
4040
4205
  const collateralDecimals = collateralPosition?.depositedDecimals ?? getAssetNativeDecimals(collateralAsset);
@@ -4054,22 +4219,28 @@ var InstantLoansModule = class {
4054
4219
  collateralAmount,
4055
4220
  decimals: collateralDecimals,
4056
4221
  asset: collateralAsset,
4057
- target: depositTarget,
4222
+ targets: depositTargetSet.targets,
4058
4223
  detectedTimestamp: input.depositDetectedTimestamp,
4059
4224
  expiryTimestamp: input.expiryTimestamp
4060
4225
  });
4226
+ const repaymentTargets = await this.createRepaymentTargetQuotes({
4227
+ baseRepaymentAmount: totalDebtAmount + interestBufferAmount,
4228
+ asset: borrowAsset,
4229
+ targets: repaymentTargetSet.targets
4230
+ });
4061
4231
  const repayment = {
4062
- amount: repaymentAmount,
4063
4232
  decimals: borrowedDecimals,
4064
4233
  debtAmount: totalDebtAmount,
4065
4234
  interestBufferAmount,
4066
4235
  interestBufferSeconds: REPAYMENT_BUFFER_SECONDS,
4067
- inflowFeeAmount: repaymentInflowFee.totalFee,
4068
- inflowFeeEstimateAvailable: repaymentInflowFee.estimateAvailable,
4069
4236
  asset: borrowAsset,
4070
- chain: repayTarget.chain,
4071
- target: repayTarget
4237
+ targets: repaymentTargets
4072
4238
  };
4239
+ const borrowIdentifier = getInstantLoanAssetIdentifier(
4240
+ borrowAsset,
4241
+ input.borrowChain,
4242
+ "borrow"
4243
+ );
4073
4244
  return {
4074
4245
  loanId: input.loanId,
4075
4246
  ref: publicIdFromInt(input.loanId),
@@ -4080,16 +4251,14 @@ var InstantLoansModule = class {
4080
4251
  depositWindowSeconds: input.depositWindowSeconds
4081
4252
  },
4082
4253
  collateral: {
4083
- poolId: collateralPoolId,
4084
4254
  asset: collateralAsset,
4085
- chain: depositTarget.chain,
4255
+ poolId: collateralPoolId,
4086
4256
  decimals: collateralDecimals,
4087
4257
  amount: collateralAmount
4088
4258
  },
4089
4259
  borrow: {
4260
+ ...borrowIdentifier,
4090
4261
  poolId: borrowPoolId,
4091
- asset: borrowAsset,
4092
- chain: repayTarget.chain,
4093
4262
  decimals: borrowedDecimals,
4094
4263
  amount: input.borrowAmount,
4095
4264
  destination: input.borrowDestination
@@ -4109,37 +4278,86 @@ var InstantLoansModule = class {
4109
4278
  };
4110
4279
  }
4111
4280
  async createInitialDepositQuote(input) {
4112
- const inflowFee = await this.lending.estimateInflowFee({
4113
- asset: input.asset,
4114
- chain: input.target.chain
4115
- });
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
+ }
4116
4293
  return {
4117
- amount: input.collateralAmount + inflowFee.totalFee,
4118
4294
  decimals: input.decimals,
4119
4295
  collateralAmount: input.collateralAmount,
4120
- inflowFeeAmount: inflowFee.totalFee,
4121
4296
  asset: input.asset,
4122
- chain: input.target.chain,
4123
- target: input.target,
4297
+ targets,
4124
4298
  detectedTimestamp: input.detectedTimestamp,
4125
4299
  expiryTimestamp: input.expiryTimestamp
4126
4300
  };
4127
4301
  }
4128
- async estimateRepaymentInflowFee(asset, chain) {
4129
- try {
4130
- const fee = await this.lending.estimateInflowFee({
4131
- asset,
4132
- chain
4133
- });
4134
- return { totalFee: fee.totalFee, estimateAvailable: true };
4135
- } catch {
4136
- return {
4137
- totalFee: getRepaymentInflowFeeFallback(asset, chain),
4138
- estimateAvailable: false
4139
- };
4140
- }
4141
- }
4142
- requireApi(action) {
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) {
4350
+ try {
4351
+ const fee = await this.lending.estimateInflowFee(target);
4352
+ return { totalFee: fee.totalFee, estimateAvailable: true };
4353
+ } catch {
4354
+ return {
4355
+ totalFee: getRepaymentInflowFeeFallback(target.asset, target.chain),
4356
+ estimateAvailable: false
4357
+ };
4358
+ }
4359
+ }
4360
+ requireApi(action) {
4143
4361
  if (!this.apiClient) {
4144
4362
  throw new LiquidiumError(
4145
4363
  LiquidiumErrorCode.VALIDATION_ERROR,
@@ -4170,10 +4388,10 @@ var InstantLoansModule = class {
4170
4388
  ]);
4171
4389
  const ltvCalculation = new QuoteModule().calculateLtv(
4172
4390
  {
4173
- borrowAmount: request.borrowAmount,
4174
- borrowPoolId: request.borrowPoolId,
4175
- collateralAmount: request.collateralAmount,
4176
- collateralPoolId: request.collateralPoolId
4391
+ borrowAmount: request.borrow.amount,
4392
+ borrowPoolId: request.borrow.poolId,
4393
+ collateralAmount: request.collateral.amount,
4394
+ collateralPoolId: request.collateral.poolId
4177
4395
  },
4178
4396
  pools,
4179
4397
  assetPrices
@@ -4195,6 +4413,15 @@ function getRepaymentInflowFeeFallback(asset, chain) {
4195
4413
  }
4196
4414
  return 0n;
4197
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
+ }
4198
4425
  function calculateInterestBufferAmount(borrowPosition, annualBorrowRate) {
4199
4426
  const totalDebtAmount = calculateTotalDebtAmount(borrowPosition);
4200
4427
  if (totalDebtAmount <= 0n || annualBorrowRate <= 0n) {
@@ -4287,13 +4514,23 @@ function deriveDepositExpiryTimestamp(input) {
4287
4514
  return input.depositDetectedTimestamp + input.depositWindowSeconds;
4288
4515
  }
4289
4516
  function validateCreateRequest(request) {
4290
- if (request.collateralAmount <= 0n) {
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) {
4291
4528
  throw new LiquidiumError(
4292
4529
  LiquidiumErrorCode.VALIDATION_ERROR,
4293
4530
  "Instant loan collateral amount must be greater than zero"
4294
4531
  );
4295
4532
  }
4296
- if (request.borrowAmount <= 0n) {
4533
+ if (request.borrow.amount <= 0n) {
4297
4534
  throw new LiquidiumError(
4298
4535
  LiquidiumErrorCode.VALIDATION_ERROR,
4299
4536
  "Instant loan borrow amount must be greater than zero"
@@ -4318,45 +4555,200 @@ function throwLtvCalculationError(error) {
4318
4555
  error?.message ?? "Unable to calculate instant loan LTV"
4319
4556
  );
4320
4557
  }
4321
- function addressFromAccountInput(account) {
4322
- const address = typeof account === "string" ? account.trim() : account.address.trim();
4323
- if (!address) {
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") {
4324
4671
  throw new LiquidiumError(
4325
4672
  LiquidiumErrorCode.VALIDATION_ERROR,
4326
- "Instant loan account address must be non-empty"
4673
+ `${params.chain} instant loan ${params.role} destination must be an external chain address for ${params.asset}`
4327
4674
  );
4328
4675
  }
4329
- return address;
4676
+ throw new LiquidiumError(
4677
+ LiquidiumErrorCode.VALIDATION_ERROR,
4678
+ `${params.asset} instant loan ${params.role} destination only supports ChainAddress or IcPrincipal destinations`
4679
+ );
4330
4680
  }
4331
- function accountFromCanister(account) {
4332
- if ("Native" in account) {
4333
- return { type: "Native", principal: account.Native.toText() };
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;
4334
4690
  }
4335
- if ("AccountIdentifier" in account) {
4336
- return {
4337
- type: "AccountIdentifier",
4338
- address: account.AccountIdentifier
4339
- };
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
+ );
4340
4708
  }
4341
- if ("Icrc" in account) {
4342
- const subaccount = normalizeOptionalSubaccount(account.Icrc.subaccount[0]);
4343
- return {
4344
- type: "Icrc",
4345
- owner: account.Icrc.owner.toText(),
4346
- subaccount,
4347
- address: icrc.encodeIcrcAccount({
4348
- owner: account.Icrc.owner,
4349
- subaccount
4350
- })
4351
- };
4709
+ try {
4710
+ return { AccountIdentifier: normalizeIcpAccountIdentifier(address) };
4711
+ } catch {
4712
+ throw new LiquidiumError(
4713
+ LiquidiumErrorCode.INVALID_ADDRESS,
4714
+ "Invalid ICP account identifier"
4715
+ );
4352
4716
  }
4353
- return { type: "External", address: account.External };
4354
4717
  }
4355
- function normalizeOptionalSubaccount(subaccount) {
4356
- if (!subaccount) {
4357
- return void 0;
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
+ );
4727
+ }
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
+ );
4358
4738
  }
4359
- return subaccount instanceof Uint8Array ? subaccount : Uint8Array.from(subaccount);
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);
4360
4752
  }
4361
4753
  function mapInstantLoanEvent(event) {
4362
4754
  return {
@@ -4373,7 +4765,10 @@ function mapInstantLoanEventType(eventType) {
4373
4765
  type: "LoanCreated",
4374
4766
  loanId: event.loan_id,
4375
4767
  borrowDestination: accountFromCanister(event.borrow_destination),
4376
- collateralAsset: event.lend_asset,
4768
+ collateralAsset: parseInstantLoanAsset(
4769
+ event.lend_asset,
4770
+ "event collateral asset"
4771
+ ),
4377
4772
  borrowAmount: event.borrow_amount,
4378
4773
  collateralPoolId: event.lend_pool_id.toText(),
4379
4774
  refundDestination: accountFromCanister(event.refund_destination),
@@ -4381,7 +4776,10 @@ function mapInstantLoanEventType(eventType) {
4381
4776
  depositWindowSeconds: event.ltv_timer_s,
4382
4777
  profileId: event.lending_profile.toText(),
4383
4778
  borrowPoolId: event.borrow_pool_id.toText(),
4384
- borrowAsset: event.borrow_asset
4779
+ borrowAsset: parseInstantLoanAsset(
4780
+ event.borrow_asset,
4781
+ "event borrow asset"
4782
+ )
4385
4783
  };
4386
4784
  }
4387
4785
  if ("FullLendWithdrawalRequested" in eventType) {
@@ -4548,6 +4946,12 @@ function mapCandidateWire(wire) {
4548
4946
  })
4549
4947
  };
4550
4948
  }
4949
+ function parseInstantLoanAsset(value, label) {
4950
+ return parseApiStringUnion(value, INSTANT_LOAN_ASSETS, {
4951
+ context: INSTANT_LOAN_WIRE_CONTEXT,
4952
+ label
4953
+ });
4954
+ }
4551
4955
  function mapInstantLoansErrorToLiquidiumError(error) {
4552
4956
  const [key, payload] = Object.entries(error)[0];
4553
4957
  switch (key) {
@@ -4689,11 +5093,25 @@ function encodeBytes32Hex(bytes) {
4689
5093
  var idlFactory4 = ({ IDL }) => IDL.Service({
4690
5094
  icrc1_fee: IDL.Func([], [IDL.Nat], ["query"])
4691
5095
  });
4692
- function createCkBtcLedgerActor(canisterContext) {
4693
- const canisterId = CK_CANISTER_IDS.btcLedger;
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
+ }
4694
5103
  return agent.Actor.createActor(idlFactory4, {
4695
- agent: canisterContext.agent,
4696
- 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"
4697
5115
  });
4698
5116
  }
4699
5117
 
@@ -4722,11 +5140,51 @@ function accountTypeToString(accountType) {
4722
5140
  switch (accountType.type) {
4723
5141
  case "External":
4724
5142
  return `Address:${accountType.data}`;
5143
+ case "Icrc":
5144
+ return `Icrc:${accountType.data}`;
5145
+ case "AccountIdentifier":
5146
+ return `AccountId:${accountType.data}`;
4725
5147
  case "Native":
4726
5148
  return `Principal:${accountType.data}`;
4727
5149
  }
4728
5150
  }
4729
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
+
4730
5188
  // src/core/withdraw-minimums.ts
4731
5189
  var MINIMUM_BTC_WITHDRAW_AMOUNT_SATS = 5000n;
4732
5190
  var MINIMUM_STABLECOIN_WITHDRAW_AMOUNT_BASE_UNITS = 1000000n;
@@ -4844,48 +5302,61 @@ var SupplyFlowExecutor = class {
4844
5302
  }
4845
5303
  params;
4846
5304
  async create(request) {
4847
- const target = await resolveSupplyTarget(
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(
4848
5313
  this.params.canisterContext,
4849
- request
5314
+ request.poolId
4850
5315
  );
4851
- const instruction = {
4852
- poolId: request.poolId,
4853
- asset: target.asset,
4854
- chain: target.chain,
4855
- action: request.action,
4856
- target
4857
- };
4858
- const mechanism = resolveSupplyMechanism({
4859
- asset: instruction.asset,
4860
- chain: instruction.chain,
4861
- requestedMechanism: request.mechanism
4862
- });
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;
4863
5328
  const defaultSubmitInflowRequest = getDefaultSubmitInflowRequest({
4864
5329
  action: request.action,
4865
- chain: mechanism === SupplyPlanType.contractInteraction ? Chain.ETH : instruction.chain
5330
+ chain: mechanism === SupplyPlanType.contractInteraction ? Chain.ETH : target.chain
4866
5331
  });
4867
5332
  let txid;
4868
5333
  switch (mechanism) {
4869
5334
  case SupplyPlanType.transfer:
4870
- if (request.walletAdapter) {
4871
- txid = await this.sendAndSubmitNativeSupplyInflow({
5335
+ if (isWalletTransferSupplyRequest(request)) {
5336
+ txid = await this.sendAndSubmitTransferSupplyInflow({
4872
5337
  request,
4873
- instruction,
5338
+ target,
4874
5339
  defaultSubmitInflowRequest
4875
5340
  });
4876
5341
  }
4877
5342
  break;
4878
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
+ }
4879
5350
  txid = await this.executeContractSupply({
4880
5351
  request,
4881
- instruction,
5352
+ target,
4882
5353
  defaultSubmitInflowRequest
4883
5354
  });
4884
5355
  break;
4885
5356
  }
4886
5357
  return {
4887
5358
  type: mechanism,
4888
- target: instruction.target,
5359
+ target,
4889
5360
  txid,
4890
5361
  status: createLiquidiumStatus({
4891
5362
  operation: mapSupplyActionToStatusOperation(request.action),
@@ -4893,7 +5364,7 @@ var SupplyFlowExecutor = class {
4893
5364
  }),
4894
5365
  submit: async (submitRequest) => {
4895
5366
  return await this.submitSupplyFlowInflow({
4896
- instruction,
5367
+ target,
4897
5368
  mechanism,
4898
5369
  defaultSubmitInflowRequest,
4899
5370
  submitRequest
@@ -4902,7 +5373,10 @@ var SupplyFlowExecutor = class {
4902
5373
  };
4903
5374
  }
4904
5375
  async getEvmSupplyContext(request) {
4905
- const selectedPool = await this.params.getPoolById(request.poolId);
5376
+ const selectedPool = await getPoolById(
5377
+ this.params.canisterContext,
5378
+ request.poolId
5379
+ );
4906
5380
  return await this.getEvmSupplyContextForPool({
4907
5381
  request,
4908
5382
  asset: selectedPool.asset,
@@ -4966,14 +5440,8 @@ var SupplyFlowExecutor = class {
4966
5440
  approvalStrategy
4967
5441
  };
4968
5442
  }
4969
- async sendAndSubmitNativeSupplyInflow(params) {
4970
- const { request, instruction, defaultSubmitInflowRequest } = params;
4971
- if (instruction.target.type !== "nativeAddress") {
4972
- throw new LiquidiumError(
4973
- LiquidiumErrorCode.VALIDATION_ERROR,
4974
- "Wallet-executed supply requires a native-address target"
4975
- );
4976
- }
5443
+ async sendAndSubmitTransferSupplyInflow(params) {
5444
+ const { request, target, defaultSubmitInflowRequest } = params;
4977
5445
  if (!request.walletAdapter) {
4978
5446
  throw new LiquidiumError(
4979
5447
  LiquidiumErrorCode.VALIDATION_ERROR,
@@ -4993,16 +5461,27 @@ var SupplyFlowExecutor = class {
4993
5461
  "Wallet-executed supply requires a positive amount"
4994
5462
  );
4995
5463
  }
4996
- const txid = await this.sendNativeSupplyTransaction({
5464
+ const txid = target.chain === Chain.ICP ? await this.sendIcrcSupplyTransaction({
4997
5465
  walletAdapter: request.walletAdapter,
4998
- chain: instruction.chain,
4999
- toAddress: instruction.target.address,
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({
5476
+ walletAdapter: request.walletAdapter,
5477
+ chain: target.chain,
5478
+ toAddress: target.address,
5000
5479
  amount: request.amount,
5001
5480
  senderAccount: account,
5002
- asset: instruction.asset,
5481
+ asset: target.asset,
5003
5482
  action: request.action
5004
5483
  });
5005
- if (shouldSubmitInflow({ instruction, mechanism: SupplyPlanType.transfer })) {
5484
+ if (shouldSubmitInflow({ target, mechanism: SupplyPlanType.transfer })) {
5006
5485
  try {
5007
5486
  await this.submitInflowWithRetry(txid, defaultSubmitInflowRequest);
5008
5487
  } catch {
@@ -5029,11 +5508,11 @@ var SupplyFlowExecutor = class {
5029
5508
  });
5030
5509
  }
5031
5510
  async executeContractSupply(params) {
5032
- const { request, instruction, defaultSubmitInflowRequest } = params;
5033
- if (instruction.target.type !== "icrcAccount" || instruction.chain !== Chain.ETH) {
5511
+ const { request, target, defaultSubmitInflowRequest } = params;
5512
+ if (!isEthStablecoin(target.asset, target.chain)) {
5034
5513
  throw new LiquidiumError(
5035
5514
  LiquidiumErrorCode.VALIDATION_ERROR,
5036
- "Contract-interaction supply is only supported for ETH ICRC-account targets"
5515
+ "Contract-interaction supply is only supported for ETH stablecoin targets"
5037
5516
  );
5038
5517
  }
5039
5518
  const walletAddressInput = request.account?.trim();
@@ -5072,8 +5551,8 @@ var SupplyFlowExecutor = class {
5072
5551
  amount: request.amount,
5073
5552
  action: request.action
5074
5553
  },
5075
- asset: instruction.asset,
5076
- chain: instruction.chain
5554
+ asset: target.asset,
5555
+ chain: target.chain
5077
5556
  });
5078
5557
  const supplyAmount = BigInt(evmSupplyContext.amount);
5079
5558
  if (BigInt(evmSupplyContext.balance) < supplyAmount) {
@@ -5129,7 +5608,7 @@ var SupplyFlowExecutor = class {
5129
5608
  amount: supplyAmount,
5130
5609
  poolId: request.poolId,
5131
5610
  profileId: request.profileId,
5132
- destinationAccount: instruction.target.account,
5611
+ destinationAccount: target.address,
5133
5612
  action: request.action
5134
5613
  }),
5135
5614
  `supply-${request.action}-deposit-erc20`
@@ -5140,7 +5619,7 @@ var SupplyFlowExecutor = class {
5140
5619
  }
5141
5620
  return depositTxid;
5142
5621
  }
5143
- async sendNativeSupplyTransaction(params) {
5622
+ async sendChainAddressSupplyTransaction(params) {
5144
5623
  switch (params.chain) {
5145
5624
  case Chain.BTC: {
5146
5625
  if (!params.walletAdapter.sendBtcTransaction) {
@@ -5154,8 +5633,7 @@ var SupplyFlowExecutor = class {
5154
5633
  toAddress: params.toAddress,
5155
5634
  amountSats: params.amount,
5156
5635
  account: params.senderAccount,
5157
- actionType: `supply-${params.action}`,
5158
- transferMode: TransferMode.native
5636
+ actionType: `supply-${params.action}`
5159
5637
  });
5160
5638
  }
5161
5639
  case Chain.ETH: {
@@ -5170,7 +5648,6 @@ var SupplyFlowExecutor = class {
5170
5648
  chain: Chain.ETH,
5171
5649
  account: params.senderAccount,
5172
5650
  actionType: `supply-${params.action}`,
5173
- transferMode: TransferMode.native,
5174
5651
  transaction: createTransferErc20Transaction({
5175
5652
  tokenAddress: getEthStablecoinContractAddress(params.asset),
5176
5653
  recipientAddress: params.toAddress,
@@ -5182,7 +5659,6 @@ var SupplyFlowExecutor = class {
5182
5659
  chain: Chain.ETH,
5183
5660
  account: params.senderAccount,
5184
5661
  actionType: `supply-${params.action}`,
5185
- transferMode: TransferMode.native,
5186
5662
  transaction: {
5187
5663
  to: params.toAddress,
5188
5664
  value: params.amount.toString()
@@ -5192,10 +5668,32 @@ var SupplyFlowExecutor = class {
5192
5668
  default:
5193
5669
  throw new LiquidiumError(
5194
5670
  LiquidiumErrorCode.VALIDATION_ERROR,
5195
- `Native-address wallet execution is not supported for ${params.chain}`
5671
+ `Chain-address wallet execution is not supported for ${params.chain}`
5196
5672
  );
5197
5673
  }
5198
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
+ }
5199
5697
  async submitInflowWithRetry(txid, extraRequest) {
5200
5698
  if (!extraRequest) {
5201
5699
  throw new LiquidiumError(
@@ -5224,7 +5722,6 @@ var SupplyFlowExecutor = class {
5224
5722
  chain: Chain.ETH,
5225
5723
  account: walletAddress,
5226
5724
  actionType,
5227
- transferMode: TransferMode.native,
5228
5725
  transaction: request
5229
5726
  });
5230
5727
  }
@@ -5313,13 +5810,19 @@ function getUnknownErrorMessage(error) {
5313
5810
  }
5314
5811
  function getDefaultSubmitInflowRequest(params) {
5315
5812
  if (params.chain !== Chain.BTC && params.chain !== Chain.ETH) {
5316
- return void 0;
5813
+ return null;
5317
5814
  }
5318
5815
  return {
5319
5816
  chain: params.chain,
5320
5817
  operation: mapSupplyActionToStatusOperation(params.action)
5321
5818
  };
5322
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
+ }
5323
5826
  function mapSupplyActionToStatusOperation(action) {
5324
5827
  return action === SupplyAction.repayment ? "repayment" : "deposit";
5325
5828
  }
@@ -5327,7 +5830,10 @@ function shouldSubmitInflow(params) {
5327
5830
  if (params.mechanism !== SupplyPlanType.transfer) {
5328
5831
  return true;
5329
5832
  }
5330
- return !isEthStablecoin(params.instruction.asset, params.instruction.chain);
5833
+ if (params.target.chain === Chain.ICP) {
5834
+ return false;
5835
+ }
5836
+ return !isEthStablecoin(params.target.asset, params.target.chain);
5331
5837
  }
5332
5838
  function mapCanisterOutflowDetails(outflow) {
5333
5839
  const rawOutflowType = getVariantKey(outflow.outflow_type);
@@ -5341,41 +5847,31 @@ function mapCanisterOutflowDetails(outflow) {
5341
5847
  };
5342
5848
  }
5343
5849
  function mapCanisterAccountType(receiver) {
5344
- if ("Native" in receiver) {
5345
- return {
5346
- type: "Native",
5347
- account: receiver.Native.toText()
5348
- };
5349
- }
5350
- if ("AccountIdentifier" in receiver) {
5351
- return {
5352
- type: "AccountIdentifier",
5353
- account: receiver.AccountIdentifier
5354
- };
5355
- }
5356
- if ("Icrc" in receiver) {
5357
- const subaccount = normalizeOptionalSubaccount2(receiver.Icrc.subaccount[0]);
5358
- return {
5359
- type: "Icrc",
5360
- owner: receiver.Icrc.owner.toText(),
5361
- subaccount,
5362
- account: icrc.encodeIcrcAccount({
5363
- owner: receiver.Icrc.owner,
5364
- subaccount
5365
- })
5366
- };
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;
5367
5862
  }
5863
+ const externalAddress = normalizeExternalAddress({
5864
+ address: parsedAccount.address,
5865
+ asset: params.asset,
5866
+ chain: params.destinationChain
5867
+ });
5368
5868
  return {
5369
- type: "External",
5370
- account: receiver.External
5869
+ address: externalAddress,
5870
+ accountType: "ChainAddress",
5871
+ canisterAccount: { External: externalAddress },
5872
+ messageAccount: { type: "External", data: externalAddress }
5371
5873
  };
5372
5874
  }
5373
- function normalizeOptionalSubaccount2(subaccount) {
5374
- if (!subaccount) {
5375
- return void 0;
5376
- }
5377
- return subaccount instanceof Uint8Array ? subaccount : Uint8Array.from(subaccount);
5378
- }
5379
5875
  function normalizeOutflowType(rawOutflowType) {
5380
5876
  switch (rawOutflowType) {
5381
5877
  case "Withdraw":
@@ -5398,6 +5894,117 @@ function mapWalletChainToLendingChain(chain) {
5398
5894
  case Chain.ETH:
5399
5895
  return { ETH: null };
5400
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
+ );
5401
6008
  }
5402
6009
 
5403
6010
  // src/modules/lending/lending.ts
@@ -5419,16 +6026,13 @@ var LendingModule = class {
5419
6026
  * @returns A signable {@link WithdrawAction} with `submit` wired to the canister.
5420
6027
  */
5421
6028
  async prepareWithdraw(request) {
5422
- const destinationAccount = request.receiverAddress.trim();
6029
+ const destination = resolveOutflowDestinationInput({
6030
+ receiver: request.receiver,
6031
+ errorMessage: "Withdraw requires a custom outflow account"
6032
+ });
5423
6033
  const signerAccount = normalizeEvmAddress(
5424
6034
  request.signerWalletAddress.trim()
5425
6035
  );
5426
- if (!destinationAccount) {
5427
- throw new LiquidiumError(
5428
- LiquidiumErrorCode.VALIDATION_ERROR,
5429
- "Withdraw requires a custom outflow account"
5430
- );
5431
- }
5432
6036
  if (!signerAccount) {
5433
6037
  throw new LiquidiumError(
5434
6038
  LiquidiumErrorCode.VALIDATION_ERROR,
@@ -5441,7 +6045,10 @@ var LendingModule = class {
5441
6045
  "Withdraw amount must be greater than 0"
5442
6046
  );
5443
6047
  }
5444
- const selectedPool = await this.getPoolById(request.poolId);
6048
+ const selectedPool = await getPoolById(
6049
+ this.canisterContext,
6050
+ request.poolId
6051
+ );
5445
6052
  const selectedAsset = selectedPool.asset;
5446
6053
  const minimumWithdrawAmountError = getWithdrawAmountMinimumValidationError({
5447
6054
  amount: request.amount,
@@ -5453,41 +6060,52 @@ var LendingModule = class {
5453
6060
  minimumWithdrawAmountError.message
5454
6061
  );
5455
6062
  }
5456
- const receiverAddress = normalizeExternalAddress({
5457
- address: destinationAccount,
6063
+ const receiver = parseOutflowDestination({
6064
+ destination,
5458
6065
  asset: selectedAsset,
5459
- chain: selectedPool.chain
6066
+ poolChain: selectedPool.chain,
6067
+ destinationChain: request.chain
5460
6068
  });
5461
6069
  const lendingActor = createLendingActor(this.canisterContext);
5462
6070
  try {
5463
6071
  const expiryTimestamp = computeExpiryTimestampFromNow();
5464
6072
  const nonce = await lendingActor.get_nonce(signerAccount);
5465
- const withdrawRequestData = {
6073
+ const withdrawActionData = {
5466
6074
  profileId: request.profileId,
5467
6075
  poolId: request.poolId,
5468
6076
  amount: request.amount,
5469
- receiverAddress,
6077
+ chain: request.chain,
6078
+ receiver: {
6079
+ address: receiver.address,
6080
+ type: receiver.accountType
6081
+ },
5470
6082
  signerWalletAddress: signerAccount,
5471
6083
  expiryTimestamp
5472
6084
  };
6085
+ const withdrawSubmissionData = {
6086
+ ...withdrawActionData,
6087
+ receiverAccount: receiver.canisterAccount
6088
+ };
5473
6089
  return {
5474
6090
  kind: WalletActionKind.createWithdraw,
5475
6091
  executionKind: WalletExecutionKind.signMessage,
5476
6092
  actionType: WalletActionKind.createWithdraw,
5477
- transferMode: TransferMode.native,
5478
6093
  account: signerAccount,
5479
6094
  message: createWithdrawAssetMessage(
5480
6095
  {
5481
6096
  pool_id: request.poolId,
5482
6097
  amount: request.amount.toString(),
5483
- account: { type: "External", data: receiverAddress },
6098
+ account: receiver.messageAccount,
5484
6099
  expiry_timestamp: expiryTimestamp
5485
6100
  },
5486
6101
  nonce
5487
6102
  ),
5488
- data: withdrawRequestData,
6103
+ data: withdrawActionData,
5489
6104
  submit: async (signatureInfo) => {
5490
- return await this.submitWithdraw(withdrawRequestData, signatureInfo);
6105
+ return await this.submitWithdraw(
6106
+ withdrawSubmissionData,
6107
+ signatureInfo
6108
+ );
5491
6109
  }
5492
6110
  };
5493
6111
  } catch (error) {
@@ -5504,7 +6122,7 @@ var LendingModule = class {
5504
6122
  {
5505
6123
  data: {
5506
6124
  expiry_timestamp: request.expiryTimestamp,
5507
- account: { External: request.receiverAddress },
6125
+ account: request.receiverAccount,
5508
6126
  pool_id: principal.Principal.fromText(request.poolId),
5509
6127
  amount: request.amount
5510
6128
  },
@@ -5560,16 +6178,13 @@ var LendingModule = class {
5560
6178
  * @returns A signable {@link BorrowAction} with `submit` wired to the canister.
5561
6179
  */
5562
6180
  async prepareBorrow(request) {
5563
- const destinationAccount = request.receiverAddress.trim();
6181
+ const destination = resolveOutflowDestinationInput({
6182
+ receiver: request.receiver,
6183
+ errorMessage: "Borrow requires a custom outflow account"
6184
+ });
5564
6185
  const signerAccount = normalizeEvmAddress(
5565
6186
  request.signerWalletAddress.trim()
5566
6187
  );
5567
- if (!destinationAccount) {
5568
- throw new LiquidiumError(
5569
- LiquidiumErrorCode.VALIDATION_ERROR,
5570
- "Borrow requires a custom outflow account"
5571
- );
5572
- }
5573
6188
  if (!signerAccount) {
5574
6189
  throw new LiquidiumError(
5575
6190
  LiquidiumErrorCode.VALIDATION_ERROR,
@@ -5582,7 +6197,10 @@ var LendingModule = class {
5582
6197
  "Borrow amount must be greater than 0"
5583
6198
  );
5584
6199
  }
5585
- const selectedPool = await this.getPoolById(request.poolId);
6200
+ const selectedPool = await getPoolById(
6201
+ this.canisterContext,
6202
+ request.poolId
6203
+ );
5586
6204
  const selectedAsset = selectedPool.asset;
5587
6205
  const minimumBorrowAmountError = getBorrowAmountMinimumValidationError({
5588
6206
  amount: request.amount,
@@ -5594,41 +6212,49 @@ var LendingModule = class {
5594
6212
  minimumBorrowAmountError.message
5595
6213
  );
5596
6214
  }
5597
- const receiverAddress = normalizeExternalAddress({
5598
- address: destinationAccount,
6215
+ const receiver = parseOutflowDestination({
6216
+ destination,
5599
6217
  asset: selectedAsset,
5600
- chain: selectedPool.chain
6218
+ poolChain: selectedPool.chain,
6219
+ destinationChain: request.chain
5601
6220
  });
5602
6221
  const lendingActor = createLendingActor(this.canisterContext);
5603
6222
  try {
5604
6223
  const expiryTimestamp = computeExpiryTimestampFromNow();
5605
6224
  const nonce = await lendingActor.get_nonce(signerAccount);
5606
- const borrowRequestData = {
6225
+ const borrowActionData = {
5607
6226
  profileId: request.profileId,
5608
6227
  poolId: request.poolId,
5609
6228
  amount: request.amount,
5610
- receiverAddress,
6229
+ chain: request.chain,
6230
+ receiver: {
6231
+ address: receiver.address,
6232
+ type: receiver.accountType
6233
+ },
5611
6234
  signerWalletAddress: signerAccount,
5612
6235
  expiryTimestamp
5613
6236
  };
6237
+ const borrowSubmissionData = {
6238
+ ...borrowActionData,
6239
+ receiverAccount: receiver.canisterAccount
6240
+ };
5614
6241
  return {
5615
6242
  kind: WalletActionKind.createBorrow,
5616
6243
  executionKind: WalletExecutionKind.signMessage,
5617
6244
  actionType: WalletActionKind.createBorrow,
5618
- transferMode: TransferMode.native,
5619
6245
  account: signerAccount,
5620
6246
  message: createBorrowAssetMessage(
5621
6247
  {
5622
6248
  pool_id: request.poolId,
5623
6249
  amount: request.amount.toString(),
5624
- account: { type: "External", data: receiverAddress },
6250
+ account: receiver.messageAccount,
5625
6251
  expiry_timestamp: expiryTimestamp
5626
6252
  },
5627
6253
  nonce
5628
6254
  ),
5629
- data: borrowRequestData,
6255
+ data: borrowActionData,
5630
6256
  submit: async (signatureInfo) => {
5631
- return await this.submitBorrow(borrowRequestData, signatureInfo);
6257
+ return await this.submitBorrow(borrowSubmissionData, signatureInfo);
5632
6258
  }
5633
6259
  };
5634
6260
  } catch (error) {
@@ -5645,7 +6271,7 @@ var LendingModule = class {
5645
6271
  ).borrow_assets(principal.Principal.fromText(request.profileId), {
5646
6272
  data: {
5647
6273
  expiry_timestamp: request.expiryTimestamp,
5648
- account: { External: request.receiverAddress },
6274
+ account: request.receiverAccount,
5649
6275
  pool_id: principal.Principal.fromText(request.poolId),
5650
6276
  amount: request.amount
5651
6277
  },
@@ -5763,11 +6389,18 @@ var LendingModule = class {
5763
6389
  *
5764
6390
  * ETH stablecoin deposit-address estimates are served by the deposit-address
5765
6391
  * canister. BTC estimates include the ckBTC minter deposit fee and ledger fee.
6392
+ * ICP-chain estimates return the corresponding ICRC ledger fee.
5766
6393
  *
5767
6394
  * @param request - Asset and chain pair to estimate for.
5768
6395
  * @returns Total fee estimate rounded up in the asset's base units.
5769
6396
  */
5770
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
+ }
5771
6404
  if (isEthStablecoin(request.asset, request.chain)) {
5772
6405
  const result = await createDepositAccountsActor(
5773
6406
  this.canisterContext
@@ -5797,6 +6430,14 @@ var LendingModule = class {
5797
6430
  ]);
5798
6431
  return { totalFee: minterFee + ledgerFee };
5799
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
+ }
5800
6441
  /**
5801
6442
  * Submits an inflow transaction id for faster indexing.
5802
6443
  *
@@ -5806,6 +6447,12 @@ var LendingModule = class {
5806
6447
  * @returns Acknowledgement including the submitted `txid`.
5807
6448
  */
5808
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
+ }
5809
6456
  const apiClient = this.requireApi();
5810
6457
  const response = await apiClient.post(SdkApiPath.inflow, request);
5811
6458
  return {
@@ -5845,30 +6492,39 @@ var LendingModule = class {
5845
6492
  return new SupplyFlowExecutor({
5846
6493
  canisterContext: this.canisterContext,
5847
6494
  evmReadClient: this.evmReadClient,
5848
- getPoolById: async (poolId) => await this.getPoolById(poolId),
5849
6495
  requireApi: () => {
5850
6496
  this.requireApi();
5851
6497
  },
5852
6498
  submitInflow: async (request) => await this.submitInflow(request)
5853
6499
  });
5854
6500
  }
5855
- async getPoolById(poolId) {
5856
- const pools = await createFlexibleLendingActor(
5857
- this.canisterContext
5858
- ).list_pools();
5859
- const selectedPool = pools.find(
5860
- (pool) => pool.principal.toText() === poolId
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}`
5861
6507
  );
5862
- const decodedPool = selectedPool ? decodeFlexiblePool(selectedPool) : null;
5863
- if (!decodedPool) {
5864
- throw new LiquidiumError(
5865
- LiquidiumErrorCode.POOL_NOT_FOUND,
5866
- `Pool not found: ${poolId}`
5867
- );
5868
- }
5869
- return decodedPool;
5870
6508
  }
5871
- };
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
+ );
6519
+ }
6520
+ if (typeof receiver === "string") {
6521
+ return address;
6522
+ }
6523
+ return {
6524
+ address,
6525
+ type: receiver.type
6526
+ };
6527
+ }
5872
6528
  function mapExpectedOutflowDetails(details, expectedOutflowType, operation) {
5873
6529
  if (details.outflowType !== expectedOutflowType) {
5874
6530
  throw new LiquidiumError(
@@ -5962,6 +6618,12 @@ function formatPrice(price, decimals) {
5962
6618
 
5963
6619
  // src/modules/market/market.ts
5964
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
+ };
5965
6627
  var MarketModule = class {
5966
6628
  constructor(canisterContext) {
5967
6629
  this.canisterContext = canisterContext;
@@ -6019,15 +6681,26 @@ var MarketModule = class {
6019
6681
  }
6020
6682
  }
6021
6683
  /**
6022
- * Resolves a single pool for the given asset and chain pair.
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.
6023
6688
  *
6024
6689
  * @param query - The market asset and chain pair to match.
6025
6690
  * @returns The single pool that matches the requested asset and chain.
6026
6691
  */
6027
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
+ }
6028
6700
  const pools = await this.listPools();
6701
+ const backingPoolChain = BACKING_POOL_CHAIN_BY_ASSET[identifier.asset];
6029
6702
  const matchedPools = pools.filter(
6030
- (pool) => pool.asset === query.asset && pool.chain === query.chain
6703
+ (pool) => pool.asset === identifier.asset && pool.chain === backingPoolChain
6031
6704
  );
6032
6705
  if (matchedPools.length === 0) {
6033
6706
  throw new LiquidiumError(
@@ -6457,8 +7130,10 @@ exports.Chain = Chain;
6457
7130
  exports.Environment = Environment;
6458
7131
  exports.EvmSupplyApprovalStrategy = EvmSupplyApprovalStrategy;
6459
7132
  exports.HistoryModule = HistoryModule;
7133
+ exports.InstantLoanCreatedError = InstantLoanCreatedError;
6460
7134
  exports.InstantLoansModule = InstantLoansModule;
6461
7135
  exports.LendingModule = LendingModule;
7136
+ exports.LiquidiumAccountType = LiquidiumAccountType;
6462
7137
  exports.LiquidiumClient = LiquidiumClient;
6463
7138
  exports.LiquidiumError = LiquidiumError;
6464
7139
  exports.LiquidiumErrorCode = LiquidiumErrorCode;
@@ -6474,7 +7149,6 @@ exports.RATE_DECIMALS = RATE_DECIMALS;
6474
7149
  exports.RATE_SCALE = RATE_SCALE2;
6475
7150
  exports.SupplyAction = SupplyAction;
6476
7151
  exports.SupplyPlanType = SupplyPlanType;
6477
- exports.TransferMode = TransferMode;
6478
7152
  exports.USDC_CONTRACT_ADDRESS = USDC_CONTRACT_ADDRESS;
6479
7153
  exports.USDT_CONTRACT_ADDRESS = USDT_CONTRACT_ADDRESS;
6480
7154
  exports.WalletActionKind = WalletActionKind;
@@ -6484,6 +7158,7 @@ exports.executeWith = executeWith;
6484
7158
  exports.getMinimumBorrowAmount = getMinimumBorrowAmount;
6485
7159
  exports.getMinimumWithdrawAmount = getMinimumWithdrawAmount;
6486
7160
  exports.intFromPublicId = intFromPublicId;
7161
+ exports.isAssetIdentifier = isAssetIdentifier;
6487
7162
  exports.publicIdFromInt = publicIdFromInt;
6488
7163
  //# sourceMappingURL=index.cjs.map
6489
7164
  //# sourceMappingURL=index.cjs.map