@liquidium/client 0.5.0 → 0.6.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
@@ -7,8 +7,8 @@ var principal = require('@icp-sdk/core/principal');
7
7
  var base = require('@scure/base');
8
8
  var candid = require('@icp-sdk/core/candid');
9
9
  var icrc = require('@icp-sdk/canisters/ledger/icrc');
10
- var icp = require('@icp-sdk/canisters/ledger/icp');
11
10
  var bitcoinAddressValidation = require('bitcoin-address-validation');
11
+ var icp = require('@icp-sdk/canisters/ledger/icp');
12
12
 
13
13
  // src/client.ts
14
14
 
@@ -88,6 +88,7 @@ var Environment = {
88
88
  };
89
89
  var Asset = {
90
90
  BTC: "BTC",
91
+ ETH: "ETH",
91
92
  ICP: "ICP",
92
93
  USDC: "USDC",
93
94
  USDT: "USDT"
@@ -102,9 +103,9 @@ function isAssetIdentifier(identifier) {
102
103
  case Chain.BTC:
103
104
  return identifier.asset === Asset.BTC;
104
105
  case Chain.ETH:
105
- return identifier.asset === Asset.USDC || identifier.asset === Asset.USDT;
106
+ return identifier.asset === Asset.ETH || identifier.asset === Asset.USDC || identifier.asset === Asset.USDT;
106
107
  case Chain.ICP:
107
- return identifier.asset === Asset.BTC || identifier.asset === Asset.ICP || identifier.asset === Asset.USDC || identifier.asset === Asset.USDT;
108
+ return identifier.asset === Asset.BTC || identifier.asset === Asset.ETH || identifier.asset === Asset.ICP || identifier.asset === Asset.USDC || identifier.asset === Asset.USDT;
108
109
  default:
109
110
  return false;
110
111
  }
@@ -127,6 +128,7 @@ var MAINNET_CANISTER_IDS = {
127
128
  lending: "hyk4r-jqaaa-aaaar-qb4ca-cai",
128
129
  pools: {
129
130
  btc: "hkmli-faaaa-aaaar-qb4ba-cai",
131
+ eth: "qcg7y-syaaa-aaaar-qb75q-cai",
130
132
  usdt: "hnnn4-iyaaa-aaaar-qb4bq-cai",
131
133
  usdc: "6sna2-oiaaa-aaaar-qb66q-cai",
132
134
  icp: "r2pk3-4yaaa-aaaar-qb7zq-cai"
@@ -145,6 +147,7 @@ var SUPPORTED_CANISTER_ID_OVERRIDE_KEYS = /* @__PURE__ */ new Set([
145
147
  ]);
146
148
  var SUPPORTED_POOL_CANISTER_ID_OVERRIDE_KEYS = /* @__PURE__ */ new Set([
147
149
  "btc",
150
+ "eth",
148
151
  "usdt",
149
152
  "usdc",
150
153
  "icp"
@@ -195,6 +198,8 @@ var CK_CANISTER_IDS = {
195
198
  BTC: {
196
199
  minter: "mqygn-kiaaa-aaaar-qaadq-cai",
197
200
  ledger: "mxzaz-hqaaa-aaaar-qaada-cai"},
201
+ ETH: {
202
+ ledger: "ss2fx-dyaaa-aaaar-qacoq-cai"},
198
203
  USDT: {
199
204
  ledger: "cngnf-vqaaa-aaaar-qag4q-cai"},
200
205
  USDC: {
@@ -433,6 +438,7 @@ var idlFactory = ({ IDL }) => {
433
438
  });
434
439
  const Assets = IDL.Variant({
435
440
  "BTC": IDL.Null,
441
+ "ETH": IDL.Null,
436
442
  "ICP": IDL.Null,
437
443
  "SOL": IDL.Null,
438
444
  "USDC": IDL.Null,
@@ -1016,6 +1022,16 @@ var ERC20_ABI = [
1016
1022
  }
1017
1023
  ];
1018
1024
  var CK_DEPOSIT_ABI = [
1025
+ {
1026
+ inputs: [
1027
+ { internalType: "bytes32", name: "principal", type: "bytes32" },
1028
+ { internalType: "bytes32", name: "subaccount", type: "bytes32" }
1029
+ ],
1030
+ name: "depositEth",
1031
+ outputs: [],
1032
+ stateMutability: "payable",
1033
+ type: "function"
1034
+ },
1019
1035
  {
1020
1036
  inputs: [
1021
1037
  { internalType: "address", name: "erc20Address", type: "address" },
@@ -1399,7 +1415,7 @@ function mapCanisterWalletToWallet(canisterWallet) {
1399
1415
  );
1400
1416
  }
1401
1417
  }
1402
- var KNOWN_ASSET_TAGS = ["BTC", "ICP", "USDC", "USDT"];
1418
+ var KNOWN_ASSET_TAGS = ["BTC", "ETH", "ICP", "USDC", "USDT"];
1403
1419
  var KNOWN_CHAIN_TAGS = ["BTC", "ETH", "ICP"];
1404
1420
  function extractVariantTag(variant, knownTags) {
1405
1421
  const [key] = Object.keys(variant);
@@ -2225,28 +2241,7 @@ function createTransferErc20Transaction(params) {
2225
2241
  };
2226
2242
  }
2227
2243
  function createDepositErc20Transaction(params) {
2228
- const expectedDestinationAccount = icrc.encodeIcrcAccount({
2229
- owner: principal.Principal.fromText(params.poolId),
2230
- subaccount: encodeInflowSubaccount({
2231
- action: params.action,
2232
- principal: principal.Principal.fromText(params.profileId)
2233
- })
2234
- });
2235
- if (params.destinationAccount !== expectedDestinationAccount) {
2236
- throw new LiquidiumError(
2237
- LiquidiumErrorCode.VALIDATION_ERROR,
2238
- "ETH supply destination account does not match the expected inflow account"
2239
- );
2240
- }
2241
- const principalBytes32 = encodePrincipalToBytes32(
2242
- principal.Principal.fromText(params.poolId)
2243
- );
2244
- const subaccountHex = encodeBytes32Hex(
2245
- encodeInflowSubaccount({
2246
- action: params.action,
2247
- principal: principal.Principal.fromText(params.profileId)
2248
- })
2249
- );
2244
+ const { principalBytes32, subaccountHex } = createDepositAccountContext(params);
2250
2245
  return {
2251
2246
  to: params.depositContractAddress,
2252
2247
  data: viem.encodeFunctionData({
@@ -2261,6 +2256,40 @@ function createDepositErc20Transaction(params) {
2261
2256
  })
2262
2257
  };
2263
2258
  }
2259
+ function createDepositEthTransaction(params) {
2260
+ const { principalBytes32, subaccountHex } = createDepositAccountContext(params);
2261
+ return {
2262
+ to: params.depositContractAddress,
2263
+ data: viem.encodeFunctionData({
2264
+ abi: CK_DEPOSIT_ABI,
2265
+ functionName: "depositEth",
2266
+ args: [principalBytes32, subaccountHex]
2267
+ }),
2268
+ value: params.amount.toString()
2269
+ };
2270
+ }
2271
+ function createDepositAccountContext(params) {
2272
+ const inflowSubaccount = encodeInflowSubaccount({
2273
+ action: params.action,
2274
+ principal: principal.Principal.fromText(params.profileId)
2275
+ });
2276
+ const expectedDestinationAccount = icrc.encodeIcrcAccount({
2277
+ owner: principal.Principal.fromText(params.poolId),
2278
+ subaccount: inflowSubaccount
2279
+ });
2280
+ if (params.destinationAccount !== expectedDestinationAccount) {
2281
+ throw new LiquidiumError(
2282
+ LiquidiumErrorCode.VALIDATION_ERROR,
2283
+ "ETH supply destination account does not match the expected inflow account"
2284
+ );
2285
+ }
2286
+ return {
2287
+ principalBytes32: encodePrincipalToBytes32(
2288
+ principal.Principal.fromText(params.poolId)
2289
+ ),
2290
+ subaccountHex: encodeBytes32Hex(inflowSubaccount)
2291
+ };
2292
+ }
2264
2293
  function encodePrincipalToBytes32(principal) {
2265
2294
  const principalBytes = principal.toUint8Array();
2266
2295
  if (principalBytes.length > 29) {
@@ -2280,12 +2309,59 @@ function encodeBytes32Hex(bytes) {
2280
2309
  (byte) => byte.toString(16).padStart(2, "0")
2281
2310
  ).join("")}`;
2282
2311
  }
2312
+ var BTC_MAINNET_ADDRESS_ERROR = "Address must be a valid mainnet BTC address";
2313
+ var EVM_ADDRESS_ERROR = "Address must be a valid EVM address";
2314
+ var ADDRESS_CHAIN_MISMATCH_ERROR = "Address chain must match asset";
2315
+ function normalizeExternalAddress(params) {
2316
+ if (params.asset === Asset.BTC) {
2317
+ if (params.chain !== Chain.BTC) {
2318
+ throw new LiquidiumError(
2319
+ LiquidiumErrorCode.INVALID_ADDRESS,
2320
+ ADDRESS_CHAIN_MISMATCH_ERROR
2321
+ );
2322
+ }
2323
+ if (!bitcoinAddressValidation.validate(params.address, bitcoinAddressValidation.Network.mainnet)) {
2324
+ throw new LiquidiumError(
2325
+ LiquidiumErrorCode.INVALID_ADDRESS,
2326
+ BTC_MAINNET_ADDRESS_ERROR
2327
+ );
2328
+ }
2329
+ return params.address;
2330
+ }
2331
+ if (isEvmAsset(params.asset)) {
2332
+ if (params.chain !== Chain.ETH) {
2333
+ throw new LiquidiumError(
2334
+ LiquidiumErrorCode.INVALID_ADDRESS,
2335
+ ADDRESS_CHAIN_MISMATCH_ERROR
2336
+ );
2337
+ }
2338
+ if (!viem.isAddress(params.address)) {
2339
+ throw new LiquidiumError(
2340
+ LiquidiumErrorCode.INVALID_ADDRESS,
2341
+ EVM_ADDRESS_ERROR
2342
+ );
2343
+ }
2344
+ return viem.getAddress(params.address);
2345
+ }
2346
+ return params.address;
2347
+ }
2348
+ function normalizeAndValidateEvmAddress(address, errorMessage) {
2349
+ if (!viem.isAddress(address)) {
2350
+ throw new LiquidiumError(LiquidiumErrorCode.INVALID_ADDRESS, errorMessage);
2351
+ }
2352
+ return viem.getAddress(address);
2353
+ }
2354
+ function isEvmAsset(asset) {
2355
+ return asset === Asset.ETH || asset === Asset.USDC || asset === Asset.USDT;
2356
+ }
2283
2357
 
2284
2358
  // src/core/borrow-minimums.ts
2285
2359
  var MINIMUM_BTC_BORROW_AMOUNT_SATS = 5100n;
2360
+ var MINIMUM_ETH_BORROW_AMOUNT_WEI = 5000000000000000n;
2286
2361
  var MINIMUM_STABLECOIN_BORROW_AMOUNT_BASE_UNITS = 1000000n;
2287
2362
  var MIN_BORROW_AMOUNTS_BY_ASSET = {
2288
2363
  [Asset.BTC]: MINIMUM_BTC_BORROW_AMOUNT_SATS,
2364
+ [Asset.ETH]: MINIMUM_ETH_BORROW_AMOUNT_WEI,
2289
2365
  [Asset.USDC]: MINIMUM_STABLECOIN_BORROW_AMOUNT_BASE_UNITS,
2290
2366
  [Asset.USDT]: MINIMUM_STABLECOIN_BORROW_AMOUNT_BASE_UNITS
2291
2367
  };
@@ -3179,6 +3255,13 @@ function getPoolLedgerAssetRoute(params) {
3179
3255
  chain: params.chain
3180
3256
  };
3181
3257
  }
3258
+ if (params.asset === Asset.ETH && (params.chain === Chain.ETH || params.chain === Chain.ICP)) {
3259
+ return {
3260
+ ledgerCanisterId: CK_CANISTER_IDS.ETH.ledger,
3261
+ asset: Asset.ETH,
3262
+ chain: params.chain
3263
+ };
3264
+ }
3182
3265
  if (params.asset === Asset.USDT && (params.chain === Chain.ETH || params.chain === Chain.ICP)) {
3183
3266
  return {
3184
3267
  ledgerCanisterId: CK_CANISTER_IDS.USDT.ledger,
@@ -3253,9 +3336,11 @@ function createLiquidiumStatus(params) {
3253
3336
 
3254
3337
  // src/core/withdraw-minimums.ts
3255
3338
  var MINIMUM_BTC_WITHDRAW_AMOUNT_SATS = 5000n;
3339
+ var MINIMUM_ETH_WITHDRAW_AMOUNT_WEI = 5000000000000000n;
3256
3340
  var MINIMUM_STABLECOIN_WITHDRAW_AMOUNT_BASE_UNITS = 1000000n;
3257
3341
  var MIN_WITHDRAW_AMOUNTS_BY_ASSET = {
3258
3342
  [Asset.BTC]: MINIMUM_BTC_WITHDRAW_AMOUNT_SATS,
3343
+ [Asset.ETH]: MINIMUM_ETH_WITHDRAW_AMOUNT_WEI,
3259
3344
  [Asset.USDC]: MINIMUM_STABLECOIN_WITHDRAW_AMOUNT_BASE_UNITS,
3260
3345
  [Asset.USDT]: MINIMUM_STABLECOIN_WITHDRAW_AMOUNT_BASE_UNITS
3261
3346
  };
@@ -3392,7 +3477,7 @@ function resolveSupplyMechanism(params) {
3392
3477
  if (params.mechanism !== SupplyPlanType.contractInteraction) {
3393
3478
  return SupplyPlanType.transfer;
3394
3479
  }
3395
- if (params.identifier.chain === Chain.ETH && (params.identifier.asset === Asset.USDC || params.identifier.asset === Asset.USDT)) {
3480
+ if (params.identifier.chain === Chain.ETH && (params.identifier.asset === Asset.ETH || params.identifier.asset === Asset.USDC || params.identifier.asset === Asset.USDT)) {
3396
3481
  return SupplyPlanType.contractInteraction;
3397
3482
  }
3398
3483
  throw new LiquidiumError(
@@ -3415,6 +3500,12 @@ function getEthStablecoinContractAddress(asset) {
3415
3500
  `ETH stablecoin contract address is not configured for ${asset}`
3416
3501
  );
3417
3502
  }
3503
+ function getEthDepositTokenAddress(asset) {
3504
+ if (asset === Asset.ETH) {
3505
+ return [];
3506
+ }
3507
+ return [getEthStablecoinContractAddress(asset)];
3508
+ }
3418
3509
  function mapDepositAccountErrorToLiquidiumError(error) {
3419
3510
  if ("InvalidEvmAddress" in error) {
3420
3511
  return new LiquidiumError(
@@ -3478,7 +3569,7 @@ function resolveSupplyAssetIdentifier(params) {
3478
3569
  return { asset: Asset.BTC, chain: Chain.ICP };
3479
3570
  }
3480
3571
  }
3481
- if ((params.asset === Asset.USDC || params.asset === Asset.USDT) && params.poolChain === Chain.ETH) {
3572
+ if ((params.asset === Asset.ETH || params.asset === Asset.USDC || params.asset === Asset.USDT) && params.poolChain === Chain.ETH) {
3482
3573
  if (params.transferChain === Chain.ETH) {
3483
3574
  return { asset: params.asset, chain: Chain.ETH };
3484
3575
  }
@@ -3496,7 +3587,6 @@ function resolveSupplyAssetIdentifier(params) {
3496
3587
  }
3497
3588
  async function getChainAddressSupplyTarget(canisterContext, profileId, request) {
3498
3589
  if (request.chain === Chain.ETH) {
3499
- const tokenAddress = getEthStablecoinContractAddress(request.asset);
3500
3590
  const subaccount2 = encodeInflowSubaccount({
3501
3591
  action: request.action,
3502
3592
  principal: principal.Principal.fromText(profileId)
@@ -3508,17 +3598,21 @@ async function getChainAddressSupplyTarget(canisterContext, profileId, request)
3508
3598
  owner: principal.Principal.fromText(request.poolId),
3509
3599
  subaccount: [subaccount2]
3510
3600
  },
3511
- [tokenAddress]
3601
+ getEthDepositTokenAddress(request.asset)
3512
3602
  );
3513
3603
  if ("Err" in result) {
3514
3604
  throw mapDepositAccountErrorToLiquidiumError(result.Err);
3515
3605
  }
3606
+ const address2 = normalizeAndValidateEvmAddress(
3607
+ result.Ok,
3608
+ "Deposit address canister returned an invalid EVM address"
3609
+ );
3516
3610
  return {
3517
3611
  poolId: request.poolId,
3518
3612
  asset: request.asset,
3519
3613
  chain: request.chain,
3520
3614
  action: request.action,
3521
- address: result.Ok
3615
+ address: address2
3522
3616
  };
3523
3617
  }
3524
3618
  const configuredBtcPoolId = canisterContext.canisterIds.pools.btc;
@@ -3597,50 +3691,38 @@ function roundInflowFeeEstimate(request, totalFee) {
3597
3691
  function roundUpToNearest(amount, roundingUnit) {
3598
3692
  return ceilDivBigint(amount, roundingUnit) * roundingUnit;
3599
3693
  }
3600
- var BTC_MAINNET_ADDRESS_ERROR = "Address must be a valid mainnet BTC address";
3601
- var EVM_ADDRESS_ERROR = "Address must be a valid EVM address";
3602
- var ADDRESS_CHAIN_MISMATCH_ERROR = "Address chain must match asset";
3603
- function normalizeExternalAddress(params) {
3604
- if (params.asset === Asset.BTC) {
3605
- if (params.chain !== Chain.BTC) {
3606
- throw new LiquidiumError(
3607
- LiquidiumErrorCode.INVALID_ADDRESS,
3608
- ADDRESS_CHAIN_MISMATCH_ERROR
3609
- );
3610
- }
3611
- if (!bitcoinAddressValidation.validate(params.address, bitcoinAddressValidation.Network.mainnet)) {
3612
- throw new LiquidiumError(
3613
- LiquidiumErrorCode.INVALID_ADDRESS,
3614
- BTC_MAINNET_ADDRESS_ERROR
3615
- );
3616
- }
3617
- return params.address;
3618
- }
3619
- if (isEthStablecoin2(params.asset)) {
3620
- if (params.chain !== Chain.ETH) {
3621
- throw new LiquidiumError(
3622
- LiquidiumErrorCode.INVALID_ADDRESS,
3623
- ADDRESS_CHAIN_MISMATCH_ERROR
3624
- );
3625
- }
3626
- if (!viem.isAddress(params.address)) {
3627
- throw new LiquidiumError(
3628
- LiquidiumErrorCode.INVALID_ADDRESS,
3629
- EVM_ADDRESS_ERROR
3630
- );
3631
- }
3632
- return viem.getAddress(params.address);
3694
+
3695
+ // src/core/deposit-minimums.ts
3696
+ var MINIMUM_BTC_DEPOSIT_AMOUNT_SATS = 5100n;
3697
+ var MINIMUM_ETH_DEPOSIT_AMOUNT_WEI = 5000000000000000n;
3698
+ var MINIMUM_ICP_DEPOSIT_AMOUNT_E8S = 10000n;
3699
+ var MINIMUM_STABLECOIN_DEPOSIT_AMOUNT_BASE_UNITS = 1000000n;
3700
+ var MIN_DEPOSIT_AMOUNTS_BY_ASSET = {
3701
+ [Asset.BTC]: MINIMUM_BTC_DEPOSIT_AMOUNT_SATS,
3702
+ [Asset.ETH]: MINIMUM_ETH_DEPOSIT_AMOUNT_WEI,
3703
+ [Asset.ICP]: MINIMUM_ICP_DEPOSIT_AMOUNT_E8S,
3704
+ [Asset.USDC]: MINIMUM_STABLECOIN_DEPOSIT_AMOUNT_BASE_UNITS,
3705
+ [Asset.USDT]: MINIMUM_STABLECOIN_DEPOSIT_AMOUNT_BASE_UNITS
3706
+ };
3707
+ function getMinimumDepositAmount(asset) {
3708
+ if (!isMinimumDepositAsset(asset)) {
3709
+ return 0n;
3633
3710
  }
3634
- return params.address;
3711
+ return MIN_DEPOSIT_AMOUNTS_BY_ASSET[asset];
3635
3712
  }
3636
- function normalizeAndValidateEvmAddress(address, errorMessage) {
3637
- if (!viem.isAddress(address)) {
3638
- throw new LiquidiumError(LiquidiumErrorCode.INVALID_ADDRESS, errorMessage);
3713
+ function getDepositAmountMinimumValidationError(params) {
3714
+ const minimumAmount = getMinimumDepositAmount(params.asset);
3715
+ if (minimumAmount <= 0n || params.amount >= minimumAmount) {
3716
+ return null;
3639
3717
  }
3640
- return viem.getAddress(address);
3718
+ return {
3719
+ asset: params.asset,
3720
+ minimumAmount,
3721
+ message: `Deposit amount must be at least ${minimumAmount} base units for ${params.asset}`
3722
+ };
3641
3723
  }
3642
- function isEthStablecoin2(asset) {
3643
- return asset === Asset.USDC || asset === Asset.USDT;
3724
+ function isMinimumDepositAsset(asset) {
3725
+ return Object.hasOwn(MIN_DEPOSIT_AMOUNTS_BY_ASSET, asset);
3644
3726
  }
3645
3727
 
3646
3728
  // src/core/utils/retry.ts
@@ -3707,7 +3789,13 @@ var SupplyFlowExecutor = class {
3707
3789
  params;
3708
3790
  async create(request) {
3709
3791
  const requestedMechanism = request.mechanism ?? null;
3710
- if (requestedMechanism === null && request.walletAdapter === void 0 && (request.account !== void 0 || request.amount !== void 0)) {
3792
+ if (requestedMechanism !== null && requestedMechanism !== SupplyPlanType.transfer && requestedMechanism !== SupplyPlanType.contractInteraction) {
3793
+ throw new LiquidiumError(
3794
+ LiquidiumErrorCode.VALIDATION_ERROR,
3795
+ `Unsupported supply mechanism: ${String(requestedMechanism)}`
3796
+ );
3797
+ }
3798
+ if (requestedMechanism !== SupplyPlanType.contractInteraction && request.walletAdapter === void 0 && (request.account !== void 0 || request.amount !== void 0)) {
3711
3799
  throw new LiquidiumError(
3712
3800
  LiquidiumErrorCode.VALIDATION_ERROR,
3713
3801
  "Wallet-executed supply requires walletAdapter, account, and amount"
@@ -3717,6 +3805,13 @@ var SupplyFlowExecutor = class {
3717
3805
  this.params.canisterContext,
3718
3806
  request.poolId
3719
3807
  );
3808
+ if (request.amount !== void 0) {
3809
+ validateDepositMinimum({
3810
+ action: request.action,
3811
+ amount: request.amount,
3812
+ asset: selectedPool.asset
3813
+ });
3814
+ }
3720
3815
  const target = await resolveSupplyTargetForPool(
3721
3816
  this.params.canisterContext,
3722
3817
  {
@@ -3781,6 +3876,11 @@ var SupplyFlowExecutor = class {
3781
3876
  this.params.canisterContext,
3782
3877
  request.poolId
3783
3878
  );
3879
+ validateDepositMinimum({
3880
+ action: request.action,
3881
+ amount: request.amount,
3882
+ asset: selectedPool.asset
3883
+ });
3784
3884
  return await this.getEvmSupplyContextForPool({
3785
3885
  request,
3786
3886
  asset: selectedPool.asset,
@@ -3852,13 +3952,17 @@ var SupplyFlowExecutor = class {
3852
3952
  "Wallet-executed supply requires a wallet adapter"
3853
3953
  );
3854
3954
  }
3855
- const account = request.account?.trim();
3856
- if (!account) {
3955
+ const accountInput = request.account?.trim();
3956
+ if (!accountInput) {
3857
3957
  throw new LiquidiumError(
3858
3958
  LiquidiumErrorCode.VALIDATION_ERROR,
3859
3959
  "Wallet-executed transfer supply requires an account"
3860
3960
  );
3861
3961
  }
3962
+ const senderAccount = target.chain === Chain.ETH ? normalizeAndValidateEvmAddress(
3963
+ accountInput,
3964
+ "Invalid EVM wallet address"
3965
+ ) : accountInput;
3862
3966
  if (!request.amount || request.amount <= 0n) {
3863
3967
  throw new LiquidiumError(
3864
3968
  LiquidiumErrorCode.VALIDATION_ERROR,
@@ -3874,14 +3978,14 @@ var SupplyFlowExecutor = class {
3874
3978
  to: decodeIcrcAccountAddress(target.address).account,
3875
3979
  amount: request.amount
3876
3980
  },
3877
- senderAccount: account,
3981
+ senderAccount,
3878
3982
  action: request.action
3879
3983
  }) : await this.sendChainAddressSupplyTransaction({
3880
3984
  walletAdapter: request.walletAdapter,
3881
3985
  chain: target.chain,
3882
3986
  toAddress: target.address,
3883
3987
  amount: request.amount,
3884
- senderAccount: account,
3988
+ senderAccount,
3885
3989
  asset: target.asset,
3886
3990
  action: request.action
3887
3991
  });
@@ -3913,10 +4017,10 @@ var SupplyFlowExecutor = class {
3913
4017
  }
3914
4018
  async executeContractSupply(params) {
3915
4019
  const { request, target, defaultSubmitInflowRequest } = params;
3916
- if (!isEthStablecoin(target.asset, target.chain)) {
4020
+ if (target.chain !== Chain.ETH || target.asset !== Asset.ETH && !isEthStablecoin(target.asset, target.chain)) {
3917
4021
  throw new LiquidiumError(
3918
4022
  LiquidiumErrorCode.VALIDATION_ERROR,
3919
- "Contract-interaction supply is only supported for ETH stablecoin targets"
4023
+ "Contract-interaction supply is only supported for native ETH and ETH stablecoin targets"
3920
4024
  );
3921
4025
  }
3922
4026
  const walletAddressInput = request.account?.trim();
@@ -3936,6 +4040,12 @@ var SupplyFlowExecutor = class {
3936
4040
  "Contract-interaction supply requires a positive amount"
3937
4041
  );
3938
4042
  }
4043
+ if (request.amount > MAX_UINT256) {
4044
+ throw new LiquidiumError(
4045
+ LiquidiumErrorCode.VALIDATION_ERROR,
4046
+ "Contract-interaction supply amount exceeds uint256 maximum"
4047
+ );
4048
+ }
3939
4049
  const walletAdapter = request.walletAdapter;
3940
4050
  if (!walletAdapter?.sendEthTransaction) {
3941
4051
  throw new LiquidiumError(
@@ -3944,6 +4054,26 @@ var SupplyFlowExecutor = class {
3944
4054
  );
3945
4055
  }
3946
4056
  this.params.requireApi();
4057
+ if (target.asset === Asset.ETH) {
4058
+ const depositTxid2 = await this.sendEthContractTransaction(
4059
+ walletAdapter,
4060
+ walletAddress,
4061
+ createDepositEthTransaction({
4062
+ depositContractAddress: CK_ETH_DEPOSIT_CONTRACT_ADDRESS,
4063
+ amount: request.amount,
4064
+ poolId: request.poolId,
4065
+ profileId: request.profileId,
4066
+ destinationAccount: target.address,
4067
+ action: request.action
4068
+ }),
4069
+ `supply-${request.action}-deposit-eth`
4070
+ );
4071
+ await this.registerContractSupplyInflow(
4072
+ depositTxid2,
4073
+ defaultSubmitInflowRequest
4074
+ );
4075
+ return depositTxid2;
4076
+ }
3947
4077
  this.requireEvmReadClient(
3948
4078
  "Contract-interaction supply requires an EVM RPC URL or public client"
3949
4079
  );
@@ -4017,11 +4147,17 @@ var SupplyFlowExecutor = class {
4017
4147
  }),
4018
4148
  `supply-${request.action}-deposit-erc20`
4019
4149
  );
4150
+ await this.registerContractSupplyInflow(
4151
+ depositTxid,
4152
+ defaultSubmitInflowRequest
4153
+ );
4154
+ return depositTxid;
4155
+ }
4156
+ async registerContractSupplyInflow(txid, defaultSubmitInflowRequest) {
4020
4157
  try {
4021
- await this.submitInflowWithRetry(depositTxid, defaultSubmitInflowRequest);
4158
+ await this.submitInflowWithRetry(txid, defaultSubmitInflowRequest);
4022
4159
  } catch {
4023
4160
  }
4024
- return depositTxid;
4025
4161
  }
4026
4162
  async sendChainAddressSupplyTransaction(params) {
4027
4163
  switch (params.chain) {
@@ -4230,6 +4366,22 @@ function isContractInteractionSupplyRequest(request) {
4230
4366
  function mapSupplyActionToStatusOperation(action) {
4231
4367
  return action === SupplyAction.repayment ? "repayment" : "deposit";
4232
4368
  }
4369
+ function validateDepositMinimum(params) {
4370
+ if (params.action !== SupplyAction.deposit) {
4371
+ return;
4372
+ }
4373
+ const minimumDepositAmountError = getDepositAmountMinimumValidationError({
4374
+ amount: params.amount,
4375
+ asset: params.asset
4376
+ });
4377
+ if (!minimumDepositAmountError) {
4378
+ return;
4379
+ }
4380
+ throw new LiquidiumError(
4381
+ LiquidiumErrorCode.VALIDATION_ERROR,
4382
+ minimumDepositAmountError.message
4383
+ );
4384
+ }
4233
4385
  function shouldSubmitInflow(params) {
4234
4386
  if (params.mechanism !== SupplyPlanType.transfer) {
4235
4387
  return true;
@@ -4760,7 +4912,7 @@ var LendingModule = class {
4760
4912
  return await this.createSupplyFlowExecutor().getEvmSupplyContext(request);
4761
4913
  }
4762
4914
  /**
4763
- * Returns the read-only deposit address for an ETH stablecoin inflow target.
4915
+ * Returns the read-only deposit address for an ETH-chain inflow target.
4764
4916
  *
4765
4917
  * This is a query call that does not create or mutate state. Use it when you
4766
4918
  * need the deposit address without hitting the authorization-gated update path.
@@ -4769,13 +4921,22 @@ var LendingModule = class {
4769
4921
  * @returns The EVM deposit address for the derived account.
4770
4922
  */
4771
4923
  async getDepositAddress(request) {
4772
- if (!isEthStablecoin(request.asset, Chain.ETH)) {
4924
+ if (request.asset !== Asset.ETH && !isEthStablecoin(request.asset, Chain.ETH)) {
4925
+ throw new LiquidiumError(
4926
+ LiquidiumErrorCode.VALIDATION_ERROR,
4927
+ "getDepositAddress is only supported for ETH-chain assets"
4928
+ );
4929
+ }
4930
+ const selectedPool = await getPoolById(
4931
+ this.canisterContext,
4932
+ request.poolId
4933
+ );
4934
+ if (selectedPool.asset !== request.asset || selectedPool.chain !== Chain.ETH) {
4773
4935
  throw new LiquidiumError(
4774
4936
  LiquidiumErrorCode.VALIDATION_ERROR,
4775
- "getDepositAddress is only supported for ETH stablecoins"
4937
+ `Deposit address asset ${request.asset} does not match pool ${request.poolId}`
4776
4938
  );
4777
4939
  }
4778
- const tokenAddress = getEthStablecoinContractAddress(request.asset);
4779
4940
  const subaccount = encodeInflowSubaccount({
4780
4941
  action: request.action,
4781
4942
  principal: principal.Principal.fromText(request.profileId)
@@ -4787,17 +4948,20 @@ var LendingModule = class {
4787
4948
  owner: principal.Principal.fromText(request.poolId),
4788
4949
  subaccount: [subaccount]
4789
4950
  },
4790
- [tokenAddress]
4951
+ getEthDepositTokenAddress(request.asset)
4791
4952
  );
4792
4953
  if ("Err" in result) {
4793
4954
  throw mapDepositAccountErrorToLiquidiumError(result.Err);
4794
4955
  }
4795
- return result.Ok;
4956
+ return normalizeAndValidateEvmAddress(
4957
+ result.Ok,
4958
+ "Deposit address canister returned an invalid EVM address"
4959
+ );
4796
4960
  }
4797
4961
  /**
4798
4962
  * Estimates the network/deposit fee for an inflow target.
4799
4963
  *
4800
- * ETH stablecoin deposit-address estimates are served by the deposit-address
4964
+ * ETH-chain deposit-address estimates are served by the deposit-address
4801
4965
  * canister. BTC estimates include the ckBTC minter deposit fee and ledger fee.
4802
4966
  * ICP-chain estimates return the corresponding ICRC ledger fee.
4803
4967
  *
@@ -4811,10 +4975,10 @@ var LendingModule = class {
4811
4975
  totalFee: roundInflowFeeEstimate(request, ledgerFee)
4812
4976
  };
4813
4977
  }
4814
- if (isEthStablecoin(request.asset, request.chain)) {
4978
+ if (request.chain === Chain.ETH && (request.asset === Asset.ETH || isEthStablecoin(request.asset, request.chain))) {
4815
4979
  const result = await createDepositAccountsActor(
4816
4980
  this.canisterContext
4817
- ).estimate_deposit_fee([getEthStablecoinContractAddress(request.asset)]);
4981
+ ).estimate_deposit_fee(getEthDepositTokenAddress(request.asset));
4818
4982
  if ("Err" in result) {
4819
4983
  throw mapDepositAccountErrorToLiquidiumError(result.Err);
4820
4984
  }
@@ -4988,6 +5152,7 @@ var RATE_DECIMALS = BigInt(RATE_SCALE.toString().length - 1);
4988
5152
  // src/core/utils/asset-decimals.ts
4989
5153
  var ASSET_NATIVE_DECIMALS = {
4990
5154
  BTC: 8n,
5155
+ ETH: 18n,
4991
5156
  ICP: 8n,
4992
5157
  USDC: 6n,
4993
5158
  USDT: 6n,
@@ -5009,8 +5174,8 @@ var DECIMAL_BASE = 10;
5009
5174
  var PAIR_SEPARATOR = "_";
5010
5175
  var USDT_SYMBOL = "USDT";
5011
5176
  function mapDecodedPoolToPool(pool, rate) {
5012
- const totalSupply = pool.total_supply_at_last_sync;
5013
- const totalDebt = pool.total_debt_at_last_sync;
5177
+ const totalSupply = pool.total_supply_at_last_sync * pool.lending_index / RATE_SCALE;
5178
+ const totalDebt = pool.total_debt_at_last_sync * pool.borrow_index / RATE_SCALE;
5014
5179
  const availableLiquidity = totalSupply > totalDebt ? totalSupply - totalDebt : 0n;
5015
5180
  return {
5016
5181
  id: pool.principal.toString(),
@@ -5077,6 +5242,7 @@ function formatPrice(price, decimals) {
5077
5242
  var ZERO_POOL_RATE = [0n, 0n, 0n];
5078
5243
  var BACKING_POOL_CHAIN_BY_ASSET = {
5079
5244
  [Asset.BTC]: Chain.BTC,
5245
+ [Asset.ETH]: Chain.ETH,
5080
5246
  [Asset.ICP]: Chain.ICP,
5081
5247
  [Asset.USDC]: Chain.ETH,
5082
5248
  [Asset.USDT]: Chain.ETH
@@ -5967,12 +6133,14 @@ var REPAYMENT_BUFFER_SECONDS = 86400n;
5967
6133
  var RATE_SCALE2 = 10n ** 27n;
5968
6134
  var SECONDS_PER_YEAR = 31536000n;
5969
6135
  var MILLISECONDS_PER_SECOND3 = 1e3;
6136
+ var ETH_NATIVE_INFLOW_FEE_FALLBACK_WEI = 250000000000000n;
5970
6137
  var ETH_STABLECOIN_INFLOW_FEE_FALLBACK = 1500000n;
5971
6138
  var SIMPLE_LOAN_MIN_SLIPPAGE_BUFFER_BPS = 200n;
5972
6139
  var SIMPLE_LOAN_FIND_QUERY_MAX_LENGTH = 256;
5973
6140
  var SIMPLE_LOAN_WIRE_CONTEXT = "simple loan";
5974
6141
  var SIMPLE_LOAN_ASSETS = [
5975
6142
  Asset.BTC,
6143
+ Asset.ETH,
5976
6144
  Asset.ICP,
5977
6145
  Asset.USDC,
5978
6146
  Asset.USDT
@@ -6403,7 +6571,7 @@ var SimpleLoansModule = class {
6403
6571
  const target = input.targets[chain];
6404
6572
  if (!target) continue;
6405
6573
  assertTargetAsset(target, input.asset);
6406
- const inflowFee = await this.lending.estimateInflowFee(target);
6574
+ const inflowFee = await this.estimateInitialDepositInflowFee(target);
6407
6575
  targets[chain] = {
6408
6576
  amount: input.collateralAmount + inflowFee.totalFee,
6409
6577
  inflowFeeAmount: inflowFee.totalFee,
@@ -6419,6 +6587,24 @@ var SimpleLoansModule = class {
6419
6587
  expiryTimestamp: input.expiryTimestamp
6420
6588
  };
6421
6589
  }
6590
+ async estimateInitialDepositInflowFee(target) {
6591
+ try {
6592
+ const fee = await this.lending.estimateInflowFee(target);
6593
+ if (shouldUseNativeEthInflowFeeFallback(target, fee.totalFee)) {
6594
+ return {
6595
+ totalFee: getRepaymentInflowFeeFallback(target.asset, target.chain)
6596
+ };
6597
+ }
6598
+ return fee;
6599
+ } catch (error) {
6600
+ if (target.asset !== Asset.ETH || target.chain !== Chain.ETH) {
6601
+ throw error;
6602
+ }
6603
+ return {
6604
+ totalFee: getRepaymentInflowFeeFallback(target.asset, target.chain)
6605
+ };
6606
+ }
6607
+ }
6422
6608
  async resolveSimpleLoanInflowTargets(request) {
6423
6609
  const selectedPool = await getPoolById(
6424
6610
  this.canisterContext,
@@ -6469,6 +6655,12 @@ var SimpleLoansModule = class {
6469
6655
  async estimateRepaymentInflowFee(target) {
6470
6656
  try {
6471
6657
  const fee = await this.lending.estimateInflowFee(target);
6658
+ if (shouldUseNativeEthInflowFeeFallback(target, fee.totalFee)) {
6659
+ return {
6660
+ totalFee: getRepaymentInflowFeeFallback(target.asset, target.chain),
6661
+ estimateAvailable: false
6662
+ };
6663
+ }
6472
6664
  return { totalFee: fee.totalFee, estimateAvailable: true };
6473
6665
  } catch {
6474
6666
  return {
@@ -6544,11 +6736,17 @@ var SimpleLoansModule = class {
6544
6736
  }
6545
6737
  };
6546
6738
  function getRepaymentInflowFeeFallback(asset, chain) {
6739
+ if (chain === Chain.ETH && asset === Asset.ETH) {
6740
+ return ETH_NATIVE_INFLOW_FEE_FALLBACK_WEI;
6741
+ }
6547
6742
  if (chain === "ETH" && (asset === "USDT" || asset === "USDC")) {
6548
6743
  return ETH_STABLECOIN_INFLOW_FEE_FALLBACK;
6549
6744
  }
6550
6745
  return 0n;
6551
6746
  }
6747
+ function shouldUseNativeEthInflowFeeFallback(target, totalFee) {
6748
+ return totalFee <= 0n && target.asset === Asset.ETH && target.chain === Chain.ETH;
6749
+ }
6552
6750
  function assertTargetAsset(target, expectedAsset) {
6553
6751
  if (target.asset === expectedAsset) {
6554
6752
  return;
@@ -6698,7 +6896,7 @@ function inferSimpleLoanDeliveryChain(destination, asset) {
6698
6896
  if (asset === Asset.BTC) {
6699
6897
  return Chain.BTC;
6700
6898
  }
6701
- if (asset === Asset.USDC || asset === Asset.USDT) {
6899
+ if (asset === Asset.ETH || asset === Asset.USDC || asset === Asset.USDT) {
6702
6900
  return Chain.ETH;
6703
6901
  }
6704
6902
  return Chain.ICP;
@@ -7246,6 +7444,7 @@ exports.LiquidiumClient = LiquidiumClient;
7246
7444
  exports.LiquidiumError = LiquidiumError;
7247
7445
  exports.LiquidiumErrorCode = LiquidiumErrorCode;
7248
7446
  exports.MIN_BORROW_AMOUNTS_BY_ASSET = MIN_BORROW_AMOUNTS_BY_ASSET;
7447
+ exports.MIN_DEPOSIT_AMOUNTS_BY_ASSET = MIN_DEPOSIT_AMOUNTS_BY_ASSET;
7249
7448
  exports.MIN_WITHDRAW_AMOUNTS_BY_ASSET = MIN_WITHDRAW_AMOUNTS_BY_ASSET;
7250
7449
  exports.MarketModule = MarketModule;
7251
7450
  exports.OutflowType = OutflowType;
@@ -7266,6 +7465,7 @@ exports.WalletExecutionKind = WalletExecutionKind;
7266
7465
  exports.createTransferErc20Transaction = createTransferErc20Transaction;
7267
7466
  exports.executeWith = executeWith;
7268
7467
  exports.getMinimumBorrowAmount = getMinimumBorrowAmount;
7468
+ exports.getMinimumDepositAmount = getMinimumDepositAmount;
7269
7469
  exports.getMinimumWithdrawAmount = getMinimumWithdrawAmount;
7270
7470
  exports.intFromPublicId = intFromPublicId;
7271
7471
  exports.isAssetIdentifier = isAssetIdentifier;