@liquidium/client 0.5.1 → 0.6.0-rc.1

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) {
@@ -4041,6 +4177,12 @@ var SupplyFlowExecutor = class {
4041
4177
  });
4042
4178
  }
4043
4179
  case Chain.ETH: {
4180
+ if (params.amount > MAX_UINT256) {
4181
+ throw new LiquidiumError(
4182
+ LiquidiumErrorCode.VALIDATION_ERROR,
4183
+ "ETH transfer amount exceeds uint256 maximum"
4184
+ );
4185
+ }
4044
4186
  if (!params.walletAdapter.sendEthTransaction) {
4045
4187
  throw new LiquidiumError(
4046
4188
  LiquidiumErrorCode.VALIDATION_ERROR,
@@ -4230,6 +4372,22 @@ function isContractInteractionSupplyRequest(request) {
4230
4372
  function mapSupplyActionToStatusOperation(action) {
4231
4373
  return action === SupplyAction.repayment ? "repayment" : "deposit";
4232
4374
  }
4375
+ function validateDepositMinimum(params) {
4376
+ if (params.action !== SupplyAction.deposit) {
4377
+ return;
4378
+ }
4379
+ const minimumDepositAmountError = getDepositAmountMinimumValidationError({
4380
+ amount: params.amount,
4381
+ asset: params.asset
4382
+ });
4383
+ if (!minimumDepositAmountError) {
4384
+ return;
4385
+ }
4386
+ throw new LiquidiumError(
4387
+ LiquidiumErrorCode.VALIDATION_ERROR,
4388
+ minimumDepositAmountError.message
4389
+ );
4390
+ }
4233
4391
  function shouldSubmitInflow(params) {
4234
4392
  if (params.mechanism !== SupplyPlanType.transfer) {
4235
4393
  return true;
@@ -4760,7 +4918,7 @@ var LendingModule = class {
4760
4918
  return await this.createSupplyFlowExecutor().getEvmSupplyContext(request);
4761
4919
  }
4762
4920
  /**
4763
- * Returns the read-only deposit address for an ETH stablecoin inflow target.
4921
+ * Returns the read-only deposit address for an ETH-chain inflow target.
4764
4922
  *
4765
4923
  * This is a query call that does not create or mutate state. Use it when you
4766
4924
  * need the deposit address without hitting the authorization-gated update path.
@@ -4769,13 +4927,22 @@ var LendingModule = class {
4769
4927
  * @returns The EVM deposit address for the derived account.
4770
4928
  */
4771
4929
  async getDepositAddress(request) {
4772
- if (!isEthStablecoin(request.asset, Chain.ETH)) {
4930
+ if (request.asset !== Asset.ETH && !isEthStablecoin(request.asset, Chain.ETH)) {
4931
+ throw new LiquidiumError(
4932
+ LiquidiumErrorCode.VALIDATION_ERROR,
4933
+ "getDepositAddress is only supported for ETH-chain assets"
4934
+ );
4935
+ }
4936
+ const selectedPool = await getPoolById(
4937
+ this.canisterContext,
4938
+ request.poolId
4939
+ );
4940
+ if (selectedPool.asset !== request.asset || selectedPool.chain !== Chain.ETH) {
4773
4941
  throw new LiquidiumError(
4774
4942
  LiquidiumErrorCode.VALIDATION_ERROR,
4775
- "getDepositAddress is only supported for ETH stablecoins"
4943
+ `Deposit address asset ${request.asset} does not match pool ${request.poolId}`
4776
4944
  );
4777
4945
  }
4778
- const tokenAddress = getEthStablecoinContractAddress(request.asset);
4779
4946
  const subaccount = encodeInflowSubaccount({
4780
4947
  action: request.action,
4781
4948
  principal: principal.Principal.fromText(request.profileId)
@@ -4787,17 +4954,20 @@ var LendingModule = class {
4787
4954
  owner: principal.Principal.fromText(request.poolId),
4788
4955
  subaccount: [subaccount]
4789
4956
  },
4790
- [tokenAddress]
4957
+ getEthDepositTokenAddress(request.asset)
4791
4958
  );
4792
4959
  if ("Err" in result) {
4793
4960
  throw mapDepositAccountErrorToLiquidiumError(result.Err);
4794
4961
  }
4795
- return result.Ok;
4962
+ return normalizeAndValidateEvmAddress(
4963
+ result.Ok,
4964
+ "Deposit address canister returned an invalid EVM address"
4965
+ );
4796
4966
  }
4797
4967
  /**
4798
4968
  * Estimates the network/deposit fee for an inflow target.
4799
4969
  *
4800
- * ETH stablecoin deposit-address estimates are served by the deposit-address
4970
+ * ETH-chain deposit-address estimates are served by the deposit-address
4801
4971
  * canister. BTC estimates include the ckBTC minter deposit fee and ledger fee.
4802
4972
  * ICP-chain estimates return the corresponding ICRC ledger fee.
4803
4973
  *
@@ -4811,10 +4981,10 @@ var LendingModule = class {
4811
4981
  totalFee: roundInflowFeeEstimate(request, ledgerFee)
4812
4982
  };
4813
4983
  }
4814
- if (isEthStablecoin(request.asset, request.chain)) {
4984
+ if (request.chain === Chain.ETH && (request.asset === Asset.ETH || isEthStablecoin(request.asset, request.chain))) {
4815
4985
  const result = await createDepositAccountsActor(
4816
4986
  this.canisterContext
4817
- ).estimate_deposit_fee([getEthStablecoinContractAddress(request.asset)]);
4987
+ ).estimate_deposit_fee(getEthDepositTokenAddress(request.asset));
4818
4988
  if ("Err" in result) {
4819
4989
  throw mapDepositAccountErrorToLiquidiumError(result.Err);
4820
4990
  }
@@ -4988,6 +5158,7 @@ var RATE_DECIMALS = BigInt(RATE_SCALE.toString().length - 1);
4988
5158
  // src/core/utils/asset-decimals.ts
4989
5159
  var ASSET_NATIVE_DECIMALS = {
4990
5160
  BTC: 8n,
5161
+ ETH: 18n,
4991
5162
  ICP: 8n,
4992
5163
  USDC: 6n,
4993
5164
  USDT: 6n,
@@ -5077,6 +5248,7 @@ function formatPrice(price, decimals) {
5077
5248
  var ZERO_POOL_RATE = [0n, 0n, 0n];
5078
5249
  var BACKING_POOL_CHAIN_BY_ASSET = {
5079
5250
  [Asset.BTC]: Chain.BTC,
5251
+ [Asset.ETH]: Chain.ETH,
5080
5252
  [Asset.ICP]: Chain.ICP,
5081
5253
  [Asset.USDC]: Chain.ETH,
5082
5254
  [Asset.USDT]: Chain.ETH
@@ -5967,12 +6139,14 @@ var REPAYMENT_BUFFER_SECONDS = 86400n;
5967
6139
  var RATE_SCALE2 = 10n ** 27n;
5968
6140
  var SECONDS_PER_YEAR = 31536000n;
5969
6141
  var MILLISECONDS_PER_SECOND3 = 1e3;
6142
+ var ETH_NATIVE_INFLOW_FEE_FALLBACK_WEI = 250000000000000n;
5970
6143
  var ETH_STABLECOIN_INFLOW_FEE_FALLBACK = 1500000n;
5971
6144
  var SIMPLE_LOAN_MIN_SLIPPAGE_BUFFER_BPS = 200n;
5972
6145
  var SIMPLE_LOAN_FIND_QUERY_MAX_LENGTH = 256;
5973
6146
  var SIMPLE_LOAN_WIRE_CONTEXT = "simple loan";
5974
6147
  var SIMPLE_LOAN_ASSETS = [
5975
6148
  Asset.BTC,
6149
+ Asset.ETH,
5976
6150
  Asset.ICP,
5977
6151
  Asset.USDC,
5978
6152
  Asset.USDT
@@ -6403,7 +6577,7 @@ var SimpleLoansModule = class {
6403
6577
  const target = input.targets[chain];
6404
6578
  if (!target) continue;
6405
6579
  assertTargetAsset(target, input.asset);
6406
- const inflowFee = await this.lending.estimateInflowFee(target);
6580
+ const inflowFee = await this.estimateInitialDepositInflowFee(target);
6407
6581
  targets[chain] = {
6408
6582
  amount: input.collateralAmount + inflowFee.totalFee,
6409
6583
  inflowFeeAmount: inflowFee.totalFee,
@@ -6419,6 +6593,24 @@ var SimpleLoansModule = class {
6419
6593
  expiryTimestamp: input.expiryTimestamp
6420
6594
  };
6421
6595
  }
6596
+ async estimateInitialDepositInflowFee(target) {
6597
+ try {
6598
+ const fee = await this.lending.estimateInflowFee(target);
6599
+ if (shouldUseNativeEthInflowFeeFallback(target, fee.totalFee)) {
6600
+ return {
6601
+ totalFee: getRepaymentInflowFeeFallback(target.asset, target.chain)
6602
+ };
6603
+ }
6604
+ return fee;
6605
+ } catch (error) {
6606
+ if (target.asset !== Asset.ETH || target.chain !== Chain.ETH) {
6607
+ throw error;
6608
+ }
6609
+ return {
6610
+ totalFee: getRepaymentInflowFeeFallback(target.asset, target.chain)
6611
+ };
6612
+ }
6613
+ }
6422
6614
  async resolveSimpleLoanInflowTargets(request) {
6423
6615
  const selectedPool = await getPoolById(
6424
6616
  this.canisterContext,
@@ -6469,6 +6661,12 @@ var SimpleLoansModule = class {
6469
6661
  async estimateRepaymentInflowFee(target) {
6470
6662
  try {
6471
6663
  const fee = await this.lending.estimateInflowFee(target);
6664
+ if (shouldUseNativeEthInflowFeeFallback(target, fee.totalFee)) {
6665
+ return {
6666
+ totalFee: getRepaymentInflowFeeFallback(target.asset, target.chain),
6667
+ estimateAvailable: false
6668
+ };
6669
+ }
6472
6670
  return { totalFee: fee.totalFee, estimateAvailable: true };
6473
6671
  } catch {
6474
6672
  return {
@@ -6544,11 +6742,17 @@ var SimpleLoansModule = class {
6544
6742
  }
6545
6743
  };
6546
6744
  function getRepaymentInflowFeeFallback(asset, chain) {
6745
+ if (chain === Chain.ETH && asset === Asset.ETH) {
6746
+ return ETH_NATIVE_INFLOW_FEE_FALLBACK_WEI;
6747
+ }
6547
6748
  if (chain === "ETH" && (asset === "USDT" || asset === "USDC")) {
6548
6749
  return ETH_STABLECOIN_INFLOW_FEE_FALLBACK;
6549
6750
  }
6550
6751
  return 0n;
6551
6752
  }
6753
+ function shouldUseNativeEthInflowFeeFallback(target, totalFee) {
6754
+ return totalFee <= 0n && target.asset === Asset.ETH && target.chain === Chain.ETH;
6755
+ }
6552
6756
  function assertTargetAsset(target, expectedAsset) {
6553
6757
  if (target.asset === expectedAsset) {
6554
6758
  return;
@@ -6666,6 +6870,16 @@ function validateCreateRequest(request) {
6666
6870
  "Simple loan collateral amount must be greater than zero"
6667
6871
  );
6668
6872
  }
6873
+ const collateralMinimumError = getDepositAmountMinimumValidationError({
6874
+ amount: request.collateral.amount,
6875
+ asset: request.collateral.asset
6876
+ });
6877
+ if (collateralMinimumError) {
6878
+ throw new LiquidiumError(
6879
+ LiquidiumErrorCode.VALIDATION_ERROR,
6880
+ collateralMinimumError.message
6881
+ );
6882
+ }
6669
6883
  if (request.borrow.amount <= 0n) {
6670
6884
  throw new LiquidiumError(
6671
6885
  LiquidiumErrorCode.VALIDATION_ERROR,
@@ -6698,7 +6912,7 @@ function inferSimpleLoanDeliveryChain(destination, asset) {
6698
6912
  if (asset === Asset.BTC) {
6699
6913
  return Chain.BTC;
6700
6914
  }
6701
- if (asset === Asset.USDC || asset === Asset.USDT) {
6915
+ if (asset === Asset.ETH || asset === Asset.USDC || asset === Asset.USDT) {
6702
6916
  return Chain.ETH;
6703
6917
  }
6704
6918
  return Chain.ICP;
@@ -7246,6 +7460,7 @@ exports.LiquidiumClient = LiquidiumClient;
7246
7460
  exports.LiquidiumError = LiquidiumError;
7247
7461
  exports.LiquidiumErrorCode = LiquidiumErrorCode;
7248
7462
  exports.MIN_BORROW_AMOUNTS_BY_ASSET = MIN_BORROW_AMOUNTS_BY_ASSET;
7463
+ exports.MIN_DEPOSIT_AMOUNTS_BY_ASSET = MIN_DEPOSIT_AMOUNTS_BY_ASSET;
7249
7464
  exports.MIN_WITHDRAW_AMOUNTS_BY_ASSET = MIN_WITHDRAW_AMOUNTS_BY_ASSET;
7250
7465
  exports.MarketModule = MarketModule;
7251
7466
  exports.OutflowType = OutflowType;
@@ -7266,6 +7481,7 @@ exports.WalletExecutionKind = WalletExecutionKind;
7266
7481
  exports.createTransferErc20Transaction = createTransferErc20Transaction;
7267
7482
  exports.executeWith = executeWith;
7268
7483
  exports.getMinimumBorrowAmount = getMinimumBorrowAmount;
7484
+ exports.getMinimumDepositAmount = getMinimumDepositAmount;
7269
7485
  exports.getMinimumWithdrawAmount = getMinimumWithdrawAmount;
7270
7486
  exports.intFromPublicId = intFromPublicId;
7271
7487
  exports.isAssetIdentifier = isAssetIdentifier;