@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.js CHANGED
@@ -5,8 +5,8 @@ import { Principal } from '@icp-sdk/core/principal';
5
5
  import { base64, base64nopad } from '@scure/base';
6
6
  import { idlLabelToId } from '@icp-sdk/core/candid';
7
7
  import { decodeIcrcAccount, encodeIcrcAccount } from '@icp-sdk/canisters/ledger/icrc';
8
- import { AccountIdentifier, SubAccount } from '@icp-sdk/canisters/ledger/icp';
9
8
  import { validate, Network } from 'bitcoin-address-validation';
9
+ import { AccountIdentifier, SubAccount } from '@icp-sdk/canisters/ledger/icp';
10
10
 
11
11
  // src/client.ts
12
12
 
@@ -86,6 +86,7 @@ var Environment = {
86
86
  };
87
87
  var Asset = {
88
88
  BTC: "BTC",
89
+ ETH: "ETH",
89
90
  ICP: "ICP",
90
91
  USDC: "USDC",
91
92
  USDT: "USDT"
@@ -100,9 +101,9 @@ function isAssetIdentifier(identifier) {
100
101
  case Chain.BTC:
101
102
  return identifier.asset === Asset.BTC;
102
103
  case Chain.ETH:
103
- return identifier.asset === Asset.USDC || identifier.asset === Asset.USDT;
104
+ return identifier.asset === Asset.ETH || identifier.asset === Asset.USDC || identifier.asset === Asset.USDT;
104
105
  case Chain.ICP:
105
- return identifier.asset === Asset.BTC || identifier.asset === Asset.ICP || identifier.asset === Asset.USDC || identifier.asset === Asset.USDT;
106
+ return identifier.asset === Asset.BTC || identifier.asset === Asset.ETH || identifier.asset === Asset.ICP || identifier.asset === Asset.USDC || identifier.asset === Asset.USDT;
106
107
  default:
107
108
  return false;
108
109
  }
@@ -125,6 +126,7 @@ var MAINNET_CANISTER_IDS = {
125
126
  lending: "hyk4r-jqaaa-aaaar-qb4ca-cai",
126
127
  pools: {
127
128
  btc: "hkmli-faaaa-aaaar-qb4ba-cai",
129
+ eth: "qcg7y-syaaa-aaaar-qb75q-cai",
128
130
  usdt: "hnnn4-iyaaa-aaaar-qb4bq-cai",
129
131
  usdc: "6sna2-oiaaa-aaaar-qb66q-cai",
130
132
  icp: "r2pk3-4yaaa-aaaar-qb7zq-cai"
@@ -143,6 +145,7 @@ var SUPPORTED_CANISTER_ID_OVERRIDE_KEYS = /* @__PURE__ */ new Set([
143
145
  ]);
144
146
  var SUPPORTED_POOL_CANISTER_ID_OVERRIDE_KEYS = /* @__PURE__ */ new Set([
145
147
  "btc",
148
+ "eth",
146
149
  "usdt",
147
150
  "usdc",
148
151
  "icp"
@@ -193,6 +196,8 @@ var CK_CANISTER_IDS = {
193
196
  BTC: {
194
197
  minter: "mqygn-kiaaa-aaaar-qaadq-cai",
195
198
  ledger: "mxzaz-hqaaa-aaaar-qaada-cai"},
199
+ ETH: {
200
+ ledger: "ss2fx-dyaaa-aaaar-qacoq-cai"},
196
201
  USDT: {
197
202
  ledger: "cngnf-vqaaa-aaaar-qag4q-cai"},
198
203
  USDC: {
@@ -431,6 +436,7 @@ var idlFactory = ({ IDL }) => {
431
436
  });
432
437
  const Assets = IDL.Variant({
433
438
  "BTC": IDL.Null,
439
+ "ETH": IDL.Null,
434
440
  "ICP": IDL.Null,
435
441
  "SOL": IDL.Null,
436
442
  "USDC": IDL.Null,
@@ -1014,6 +1020,16 @@ var ERC20_ABI = [
1014
1020
  }
1015
1021
  ];
1016
1022
  var CK_DEPOSIT_ABI = [
1023
+ {
1024
+ inputs: [
1025
+ { internalType: "bytes32", name: "principal", type: "bytes32" },
1026
+ { internalType: "bytes32", name: "subaccount", type: "bytes32" }
1027
+ ],
1028
+ name: "depositEth",
1029
+ outputs: [],
1030
+ stateMutability: "payable",
1031
+ type: "function"
1032
+ },
1017
1033
  {
1018
1034
  inputs: [
1019
1035
  { internalType: "address", name: "erc20Address", type: "address" },
@@ -1397,7 +1413,7 @@ function mapCanisterWalletToWallet(canisterWallet) {
1397
1413
  );
1398
1414
  }
1399
1415
  }
1400
- var KNOWN_ASSET_TAGS = ["BTC", "ICP", "USDC", "USDT"];
1416
+ var KNOWN_ASSET_TAGS = ["BTC", "ETH", "ICP", "USDC", "USDT"];
1401
1417
  var KNOWN_CHAIN_TAGS = ["BTC", "ETH", "ICP"];
1402
1418
  function extractVariantTag(variant, knownTags) {
1403
1419
  const [key] = Object.keys(variant);
@@ -2223,28 +2239,7 @@ function createTransferErc20Transaction(params) {
2223
2239
  };
2224
2240
  }
2225
2241
  function createDepositErc20Transaction(params) {
2226
- const expectedDestinationAccount = encodeIcrcAccount({
2227
- owner: Principal.fromText(params.poolId),
2228
- subaccount: encodeInflowSubaccount({
2229
- action: params.action,
2230
- principal: Principal.fromText(params.profileId)
2231
- })
2232
- });
2233
- if (params.destinationAccount !== expectedDestinationAccount) {
2234
- throw new LiquidiumError(
2235
- LiquidiumErrorCode.VALIDATION_ERROR,
2236
- "ETH supply destination account does not match the expected inflow account"
2237
- );
2238
- }
2239
- const principalBytes32 = encodePrincipalToBytes32(
2240
- Principal.fromText(params.poolId)
2241
- );
2242
- const subaccountHex = encodeBytes32Hex(
2243
- encodeInflowSubaccount({
2244
- action: params.action,
2245
- principal: Principal.fromText(params.profileId)
2246
- })
2247
- );
2242
+ const { principalBytes32, subaccountHex } = createDepositAccountContext(params);
2248
2243
  return {
2249
2244
  to: params.depositContractAddress,
2250
2245
  data: encodeFunctionData({
@@ -2259,6 +2254,40 @@ function createDepositErc20Transaction(params) {
2259
2254
  })
2260
2255
  };
2261
2256
  }
2257
+ function createDepositEthTransaction(params) {
2258
+ const { principalBytes32, subaccountHex } = createDepositAccountContext(params);
2259
+ return {
2260
+ to: params.depositContractAddress,
2261
+ data: encodeFunctionData({
2262
+ abi: CK_DEPOSIT_ABI,
2263
+ functionName: "depositEth",
2264
+ args: [principalBytes32, subaccountHex]
2265
+ }),
2266
+ value: params.amount.toString()
2267
+ };
2268
+ }
2269
+ function createDepositAccountContext(params) {
2270
+ const inflowSubaccount = encodeInflowSubaccount({
2271
+ action: params.action,
2272
+ principal: Principal.fromText(params.profileId)
2273
+ });
2274
+ const expectedDestinationAccount = encodeIcrcAccount({
2275
+ owner: Principal.fromText(params.poolId),
2276
+ subaccount: inflowSubaccount
2277
+ });
2278
+ if (params.destinationAccount !== expectedDestinationAccount) {
2279
+ throw new LiquidiumError(
2280
+ LiquidiumErrorCode.VALIDATION_ERROR,
2281
+ "ETH supply destination account does not match the expected inflow account"
2282
+ );
2283
+ }
2284
+ return {
2285
+ principalBytes32: encodePrincipalToBytes32(
2286
+ Principal.fromText(params.poolId)
2287
+ ),
2288
+ subaccountHex: encodeBytes32Hex(inflowSubaccount)
2289
+ };
2290
+ }
2262
2291
  function encodePrincipalToBytes32(principal) {
2263
2292
  const principalBytes = principal.toUint8Array();
2264
2293
  if (principalBytes.length > 29) {
@@ -2278,12 +2307,59 @@ function encodeBytes32Hex(bytes) {
2278
2307
  (byte) => byte.toString(16).padStart(2, "0")
2279
2308
  ).join("")}`;
2280
2309
  }
2310
+ var BTC_MAINNET_ADDRESS_ERROR = "Address must be a valid mainnet BTC address";
2311
+ var EVM_ADDRESS_ERROR = "Address must be a valid EVM address";
2312
+ var ADDRESS_CHAIN_MISMATCH_ERROR = "Address chain must match asset";
2313
+ function normalizeExternalAddress(params) {
2314
+ if (params.asset === Asset.BTC) {
2315
+ if (params.chain !== Chain.BTC) {
2316
+ throw new LiquidiumError(
2317
+ LiquidiumErrorCode.INVALID_ADDRESS,
2318
+ ADDRESS_CHAIN_MISMATCH_ERROR
2319
+ );
2320
+ }
2321
+ if (!validate(params.address, Network.mainnet)) {
2322
+ throw new LiquidiumError(
2323
+ LiquidiumErrorCode.INVALID_ADDRESS,
2324
+ BTC_MAINNET_ADDRESS_ERROR
2325
+ );
2326
+ }
2327
+ return params.address;
2328
+ }
2329
+ if (isEvmAsset(params.asset)) {
2330
+ if (params.chain !== Chain.ETH) {
2331
+ throw new LiquidiumError(
2332
+ LiquidiumErrorCode.INVALID_ADDRESS,
2333
+ ADDRESS_CHAIN_MISMATCH_ERROR
2334
+ );
2335
+ }
2336
+ if (!isAddress(params.address)) {
2337
+ throw new LiquidiumError(
2338
+ LiquidiumErrorCode.INVALID_ADDRESS,
2339
+ EVM_ADDRESS_ERROR
2340
+ );
2341
+ }
2342
+ return getAddress(params.address);
2343
+ }
2344
+ return params.address;
2345
+ }
2346
+ function normalizeAndValidateEvmAddress(address, errorMessage) {
2347
+ if (!isAddress(address)) {
2348
+ throw new LiquidiumError(LiquidiumErrorCode.INVALID_ADDRESS, errorMessage);
2349
+ }
2350
+ return getAddress(address);
2351
+ }
2352
+ function isEvmAsset(asset) {
2353
+ return asset === Asset.ETH || asset === Asset.USDC || asset === Asset.USDT;
2354
+ }
2281
2355
 
2282
2356
  // src/core/borrow-minimums.ts
2283
2357
  var MINIMUM_BTC_BORROW_AMOUNT_SATS = 5100n;
2358
+ var MINIMUM_ETH_BORROW_AMOUNT_WEI = 5000000000000000n;
2284
2359
  var MINIMUM_STABLECOIN_BORROW_AMOUNT_BASE_UNITS = 1000000n;
2285
2360
  var MIN_BORROW_AMOUNTS_BY_ASSET = {
2286
2361
  [Asset.BTC]: MINIMUM_BTC_BORROW_AMOUNT_SATS,
2362
+ [Asset.ETH]: MINIMUM_ETH_BORROW_AMOUNT_WEI,
2287
2363
  [Asset.USDC]: MINIMUM_STABLECOIN_BORROW_AMOUNT_BASE_UNITS,
2288
2364
  [Asset.USDT]: MINIMUM_STABLECOIN_BORROW_AMOUNT_BASE_UNITS
2289
2365
  };
@@ -3177,6 +3253,13 @@ function getPoolLedgerAssetRoute(params) {
3177
3253
  chain: params.chain
3178
3254
  };
3179
3255
  }
3256
+ if (params.asset === Asset.ETH && (params.chain === Chain.ETH || params.chain === Chain.ICP)) {
3257
+ return {
3258
+ ledgerCanisterId: CK_CANISTER_IDS.ETH.ledger,
3259
+ asset: Asset.ETH,
3260
+ chain: params.chain
3261
+ };
3262
+ }
3180
3263
  if (params.asset === Asset.USDT && (params.chain === Chain.ETH || params.chain === Chain.ICP)) {
3181
3264
  return {
3182
3265
  ledgerCanisterId: CK_CANISTER_IDS.USDT.ledger,
@@ -3251,9 +3334,11 @@ function createLiquidiumStatus(params) {
3251
3334
 
3252
3335
  // src/core/withdraw-minimums.ts
3253
3336
  var MINIMUM_BTC_WITHDRAW_AMOUNT_SATS = 5000n;
3337
+ var MINIMUM_ETH_WITHDRAW_AMOUNT_WEI = 5000000000000000n;
3254
3338
  var MINIMUM_STABLECOIN_WITHDRAW_AMOUNT_BASE_UNITS = 1000000n;
3255
3339
  var MIN_WITHDRAW_AMOUNTS_BY_ASSET = {
3256
3340
  [Asset.BTC]: MINIMUM_BTC_WITHDRAW_AMOUNT_SATS,
3341
+ [Asset.ETH]: MINIMUM_ETH_WITHDRAW_AMOUNT_WEI,
3257
3342
  [Asset.USDC]: MINIMUM_STABLECOIN_WITHDRAW_AMOUNT_BASE_UNITS,
3258
3343
  [Asset.USDT]: MINIMUM_STABLECOIN_WITHDRAW_AMOUNT_BASE_UNITS
3259
3344
  };
@@ -3390,7 +3475,7 @@ function resolveSupplyMechanism(params) {
3390
3475
  if (params.mechanism !== SupplyPlanType.contractInteraction) {
3391
3476
  return SupplyPlanType.transfer;
3392
3477
  }
3393
- if (params.identifier.chain === Chain.ETH && (params.identifier.asset === Asset.USDC || params.identifier.asset === Asset.USDT)) {
3478
+ if (params.identifier.chain === Chain.ETH && (params.identifier.asset === Asset.ETH || params.identifier.asset === Asset.USDC || params.identifier.asset === Asset.USDT)) {
3394
3479
  return SupplyPlanType.contractInteraction;
3395
3480
  }
3396
3481
  throw new LiquidiumError(
@@ -3413,6 +3498,12 @@ function getEthStablecoinContractAddress(asset) {
3413
3498
  `ETH stablecoin contract address is not configured for ${asset}`
3414
3499
  );
3415
3500
  }
3501
+ function getEthDepositTokenAddress(asset) {
3502
+ if (asset === Asset.ETH) {
3503
+ return [];
3504
+ }
3505
+ return [getEthStablecoinContractAddress(asset)];
3506
+ }
3416
3507
  function mapDepositAccountErrorToLiquidiumError(error) {
3417
3508
  if ("InvalidEvmAddress" in error) {
3418
3509
  return new LiquidiumError(
@@ -3476,7 +3567,7 @@ function resolveSupplyAssetIdentifier(params) {
3476
3567
  return { asset: Asset.BTC, chain: Chain.ICP };
3477
3568
  }
3478
3569
  }
3479
- if ((params.asset === Asset.USDC || params.asset === Asset.USDT) && params.poolChain === Chain.ETH) {
3570
+ if ((params.asset === Asset.ETH || params.asset === Asset.USDC || params.asset === Asset.USDT) && params.poolChain === Chain.ETH) {
3480
3571
  if (params.transferChain === Chain.ETH) {
3481
3572
  return { asset: params.asset, chain: Chain.ETH };
3482
3573
  }
@@ -3494,7 +3585,6 @@ function resolveSupplyAssetIdentifier(params) {
3494
3585
  }
3495
3586
  async function getChainAddressSupplyTarget(canisterContext, profileId, request) {
3496
3587
  if (request.chain === Chain.ETH) {
3497
- const tokenAddress = getEthStablecoinContractAddress(request.asset);
3498
3588
  const subaccount2 = encodeInflowSubaccount({
3499
3589
  action: request.action,
3500
3590
  principal: Principal.fromText(profileId)
@@ -3506,17 +3596,21 @@ async function getChainAddressSupplyTarget(canisterContext, profileId, request)
3506
3596
  owner: Principal.fromText(request.poolId),
3507
3597
  subaccount: [subaccount2]
3508
3598
  },
3509
- [tokenAddress]
3599
+ getEthDepositTokenAddress(request.asset)
3510
3600
  );
3511
3601
  if ("Err" in result) {
3512
3602
  throw mapDepositAccountErrorToLiquidiumError(result.Err);
3513
3603
  }
3604
+ const address2 = normalizeAndValidateEvmAddress(
3605
+ result.Ok,
3606
+ "Deposit address canister returned an invalid EVM address"
3607
+ );
3514
3608
  return {
3515
3609
  poolId: request.poolId,
3516
3610
  asset: request.asset,
3517
3611
  chain: request.chain,
3518
3612
  action: request.action,
3519
- address: result.Ok
3613
+ address: address2
3520
3614
  };
3521
3615
  }
3522
3616
  const configuredBtcPoolId = canisterContext.canisterIds.pools.btc;
@@ -3595,50 +3689,38 @@ function roundInflowFeeEstimate(request, totalFee) {
3595
3689
  function roundUpToNearest(amount, roundingUnit) {
3596
3690
  return ceilDivBigint(amount, roundingUnit) * roundingUnit;
3597
3691
  }
3598
- var BTC_MAINNET_ADDRESS_ERROR = "Address must be a valid mainnet BTC address";
3599
- var EVM_ADDRESS_ERROR = "Address must be a valid EVM address";
3600
- var ADDRESS_CHAIN_MISMATCH_ERROR = "Address chain must match asset";
3601
- function normalizeExternalAddress(params) {
3602
- if (params.asset === Asset.BTC) {
3603
- if (params.chain !== Chain.BTC) {
3604
- throw new LiquidiumError(
3605
- LiquidiumErrorCode.INVALID_ADDRESS,
3606
- ADDRESS_CHAIN_MISMATCH_ERROR
3607
- );
3608
- }
3609
- if (!validate(params.address, Network.mainnet)) {
3610
- throw new LiquidiumError(
3611
- LiquidiumErrorCode.INVALID_ADDRESS,
3612
- BTC_MAINNET_ADDRESS_ERROR
3613
- );
3614
- }
3615
- return params.address;
3616
- }
3617
- if (isEthStablecoin2(params.asset)) {
3618
- if (params.chain !== Chain.ETH) {
3619
- throw new LiquidiumError(
3620
- LiquidiumErrorCode.INVALID_ADDRESS,
3621
- ADDRESS_CHAIN_MISMATCH_ERROR
3622
- );
3623
- }
3624
- if (!isAddress(params.address)) {
3625
- throw new LiquidiumError(
3626
- LiquidiumErrorCode.INVALID_ADDRESS,
3627
- EVM_ADDRESS_ERROR
3628
- );
3629
- }
3630
- return getAddress(params.address);
3692
+
3693
+ // src/core/deposit-minimums.ts
3694
+ var MINIMUM_BTC_DEPOSIT_AMOUNT_SATS = 5100n;
3695
+ var MINIMUM_ETH_DEPOSIT_AMOUNT_WEI = 5000000000000000n;
3696
+ var MINIMUM_ICP_DEPOSIT_AMOUNT_E8S = 10000n;
3697
+ var MINIMUM_STABLECOIN_DEPOSIT_AMOUNT_BASE_UNITS = 1000000n;
3698
+ var MIN_DEPOSIT_AMOUNTS_BY_ASSET = {
3699
+ [Asset.BTC]: MINIMUM_BTC_DEPOSIT_AMOUNT_SATS,
3700
+ [Asset.ETH]: MINIMUM_ETH_DEPOSIT_AMOUNT_WEI,
3701
+ [Asset.ICP]: MINIMUM_ICP_DEPOSIT_AMOUNT_E8S,
3702
+ [Asset.USDC]: MINIMUM_STABLECOIN_DEPOSIT_AMOUNT_BASE_UNITS,
3703
+ [Asset.USDT]: MINIMUM_STABLECOIN_DEPOSIT_AMOUNT_BASE_UNITS
3704
+ };
3705
+ function getMinimumDepositAmount(asset) {
3706
+ if (!isMinimumDepositAsset(asset)) {
3707
+ return 0n;
3631
3708
  }
3632
- return params.address;
3709
+ return MIN_DEPOSIT_AMOUNTS_BY_ASSET[asset];
3633
3710
  }
3634
- function normalizeAndValidateEvmAddress(address, errorMessage) {
3635
- if (!isAddress(address)) {
3636
- throw new LiquidiumError(LiquidiumErrorCode.INVALID_ADDRESS, errorMessage);
3711
+ function getDepositAmountMinimumValidationError(params) {
3712
+ const minimumAmount = getMinimumDepositAmount(params.asset);
3713
+ if (minimumAmount <= 0n || params.amount >= minimumAmount) {
3714
+ return null;
3637
3715
  }
3638
- return getAddress(address);
3716
+ return {
3717
+ asset: params.asset,
3718
+ minimumAmount,
3719
+ message: `Deposit amount must be at least ${minimumAmount} base units for ${params.asset}`
3720
+ };
3639
3721
  }
3640
- function isEthStablecoin2(asset) {
3641
- return asset === Asset.USDC || asset === Asset.USDT;
3722
+ function isMinimumDepositAsset(asset) {
3723
+ return Object.hasOwn(MIN_DEPOSIT_AMOUNTS_BY_ASSET, asset);
3642
3724
  }
3643
3725
 
3644
3726
  // src/core/utils/retry.ts
@@ -3705,7 +3787,13 @@ var SupplyFlowExecutor = class {
3705
3787
  params;
3706
3788
  async create(request) {
3707
3789
  const requestedMechanism = request.mechanism ?? null;
3708
- if (requestedMechanism === null && request.walletAdapter === void 0 && (request.account !== void 0 || request.amount !== void 0)) {
3790
+ if (requestedMechanism !== null && requestedMechanism !== SupplyPlanType.transfer && requestedMechanism !== SupplyPlanType.contractInteraction) {
3791
+ throw new LiquidiumError(
3792
+ LiquidiumErrorCode.VALIDATION_ERROR,
3793
+ `Unsupported supply mechanism: ${String(requestedMechanism)}`
3794
+ );
3795
+ }
3796
+ if (requestedMechanism !== SupplyPlanType.contractInteraction && request.walletAdapter === void 0 && (request.account !== void 0 || request.amount !== void 0)) {
3709
3797
  throw new LiquidiumError(
3710
3798
  LiquidiumErrorCode.VALIDATION_ERROR,
3711
3799
  "Wallet-executed supply requires walletAdapter, account, and amount"
@@ -3715,6 +3803,13 @@ var SupplyFlowExecutor = class {
3715
3803
  this.params.canisterContext,
3716
3804
  request.poolId
3717
3805
  );
3806
+ if (request.amount !== void 0) {
3807
+ validateDepositMinimum({
3808
+ action: request.action,
3809
+ amount: request.amount,
3810
+ asset: selectedPool.asset
3811
+ });
3812
+ }
3718
3813
  const target = await resolveSupplyTargetForPool(
3719
3814
  this.params.canisterContext,
3720
3815
  {
@@ -3779,6 +3874,11 @@ var SupplyFlowExecutor = class {
3779
3874
  this.params.canisterContext,
3780
3875
  request.poolId
3781
3876
  );
3877
+ validateDepositMinimum({
3878
+ action: request.action,
3879
+ amount: request.amount,
3880
+ asset: selectedPool.asset
3881
+ });
3782
3882
  return await this.getEvmSupplyContextForPool({
3783
3883
  request,
3784
3884
  asset: selectedPool.asset,
@@ -3850,13 +3950,17 @@ var SupplyFlowExecutor = class {
3850
3950
  "Wallet-executed supply requires a wallet adapter"
3851
3951
  );
3852
3952
  }
3853
- const account = request.account?.trim();
3854
- if (!account) {
3953
+ const accountInput = request.account?.trim();
3954
+ if (!accountInput) {
3855
3955
  throw new LiquidiumError(
3856
3956
  LiquidiumErrorCode.VALIDATION_ERROR,
3857
3957
  "Wallet-executed transfer supply requires an account"
3858
3958
  );
3859
3959
  }
3960
+ const senderAccount = target.chain === Chain.ETH ? normalizeAndValidateEvmAddress(
3961
+ accountInput,
3962
+ "Invalid EVM wallet address"
3963
+ ) : accountInput;
3860
3964
  if (!request.amount || request.amount <= 0n) {
3861
3965
  throw new LiquidiumError(
3862
3966
  LiquidiumErrorCode.VALIDATION_ERROR,
@@ -3872,14 +3976,14 @@ var SupplyFlowExecutor = class {
3872
3976
  to: decodeIcrcAccountAddress(target.address).account,
3873
3977
  amount: request.amount
3874
3978
  },
3875
- senderAccount: account,
3979
+ senderAccount,
3876
3980
  action: request.action
3877
3981
  }) : await this.sendChainAddressSupplyTransaction({
3878
3982
  walletAdapter: request.walletAdapter,
3879
3983
  chain: target.chain,
3880
3984
  toAddress: target.address,
3881
3985
  amount: request.amount,
3882
- senderAccount: account,
3986
+ senderAccount,
3883
3987
  asset: target.asset,
3884
3988
  action: request.action
3885
3989
  });
@@ -3911,10 +4015,10 @@ var SupplyFlowExecutor = class {
3911
4015
  }
3912
4016
  async executeContractSupply(params) {
3913
4017
  const { request, target, defaultSubmitInflowRequest } = params;
3914
- if (!isEthStablecoin(target.asset, target.chain)) {
4018
+ if (target.chain !== Chain.ETH || target.asset !== Asset.ETH && !isEthStablecoin(target.asset, target.chain)) {
3915
4019
  throw new LiquidiumError(
3916
4020
  LiquidiumErrorCode.VALIDATION_ERROR,
3917
- "Contract-interaction supply is only supported for ETH stablecoin targets"
4021
+ "Contract-interaction supply is only supported for native ETH and ETH stablecoin targets"
3918
4022
  );
3919
4023
  }
3920
4024
  const walletAddressInput = request.account?.trim();
@@ -3934,6 +4038,12 @@ var SupplyFlowExecutor = class {
3934
4038
  "Contract-interaction supply requires a positive amount"
3935
4039
  );
3936
4040
  }
4041
+ if (request.amount > MAX_UINT256) {
4042
+ throw new LiquidiumError(
4043
+ LiquidiumErrorCode.VALIDATION_ERROR,
4044
+ "Contract-interaction supply amount exceeds uint256 maximum"
4045
+ );
4046
+ }
3937
4047
  const walletAdapter = request.walletAdapter;
3938
4048
  if (!walletAdapter?.sendEthTransaction) {
3939
4049
  throw new LiquidiumError(
@@ -3942,6 +4052,26 @@ var SupplyFlowExecutor = class {
3942
4052
  );
3943
4053
  }
3944
4054
  this.params.requireApi();
4055
+ if (target.asset === Asset.ETH) {
4056
+ const depositTxid2 = await this.sendEthContractTransaction(
4057
+ walletAdapter,
4058
+ walletAddress,
4059
+ createDepositEthTransaction({
4060
+ depositContractAddress: CK_ETH_DEPOSIT_CONTRACT_ADDRESS,
4061
+ amount: request.amount,
4062
+ poolId: request.poolId,
4063
+ profileId: request.profileId,
4064
+ destinationAccount: target.address,
4065
+ action: request.action
4066
+ }),
4067
+ `supply-${request.action}-deposit-eth`
4068
+ );
4069
+ await this.registerContractSupplyInflow(
4070
+ depositTxid2,
4071
+ defaultSubmitInflowRequest
4072
+ );
4073
+ return depositTxid2;
4074
+ }
3945
4075
  this.requireEvmReadClient(
3946
4076
  "Contract-interaction supply requires an EVM RPC URL or public client"
3947
4077
  );
@@ -4015,11 +4145,17 @@ var SupplyFlowExecutor = class {
4015
4145
  }),
4016
4146
  `supply-${request.action}-deposit-erc20`
4017
4147
  );
4148
+ await this.registerContractSupplyInflow(
4149
+ depositTxid,
4150
+ defaultSubmitInflowRequest
4151
+ );
4152
+ return depositTxid;
4153
+ }
4154
+ async registerContractSupplyInflow(txid, defaultSubmitInflowRequest) {
4018
4155
  try {
4019
- await this.submitInflowWithRetry(depositTxid, defaultSubmitInflowRequest);
4156
+ await this.submitInflowWithRetry(txid, defaultSubmitInflowRequest);
4020
4157
  } catch {
4021
4158
  }
4022
- return depositTxid;
4023
4159
  }
4024
4160
  async sendChainAddressSupplyTransaction(params) {
4025
4161
  switch (params.chain) {
@@ -4039,6 +4175,12 @@ var SupplyFlowExecutor = class {
4039
4175
  });
4040
4176
  }
4041
4177
  case Chain.ETH: {
4178
+ if (params.amount > MAX_UINT256) {
4179
+ throw new LiquidiumError(
4180
+ LiquidiumErrorCode.VALIDATION_ERROR,
4181
+ "ETH transfer amount exceeds uint256 maximum"
4182
+ );
4183
+ }
4042
4184
  if (!params.walletAdapter.sendEthTransaction) {
4043
4185
  throw new LiquidiumError(
4044
4186
  LiquidiumErrorCode.VALIDATION_ERROR,
@@ -4228,6 +4370,22 @@ function isContractInteractionSupplyRequest(request) {
4228
4370
  function mapSupplyActionToStatusOperation(action) {
4229
4371
  return action === SupplyAction.repayment ? "repayment" : "deposit";
4230
4372
  }
4373
+ function validateDepositMinimum(params) {
4374
+ if (params.action !== SupplyAction.deposit) {
4375
+ return;
4376
+ }
4377
+ const minimumDepositAmountError = getDepositAmountMinimumValidationError({
4378
+ amount: params.amount,
4379
+ asset: params.asset
4380
+ });
4381
+ if (!minimumDepositAmountError) {
4382
+ return;
4383
+ }
4384
+ throw new LiquidiumError(
4385
+ LiquidiumErrorCode.VALIDATION_ERROR,
4386
+ minimumDepositAmountError.message
4387
+ );
4388
+ }
4231
4389
  function shouldSubmitInflow(params) {
4232
4390
  if (params.mechanism !== SupplyPlanType.transfer) {
4233
4391
  return true;
@@ -4758,7 +4916,7 @@ var LendingModule = class {
4758
4916
  return await this.createSupplyFlowExecutor().getEvmSupplyContext(request);
4759
4917
  }
4760
4918
  /**
4761
- * Returns the read-only deposit address for an ETH stablecoin inflow target.
4919
+ * Returns the read-only deposit address for an ETH-chain inflow target.
4762
4920
  *
4763
4921
  * This is a query call that does not create or mutate state. Use it when you
4764
4922
  * need the deposit address without hitting the authorization-gated update path.
@@ -4767,13 +4925,22 @@ var LendingModule = class {
4767
4925
  * @returns The EVM deposit address for the derived account.
4768
4926
  */
4769
4927
  async getDepositAddress(request) {
4770
- if (!isEthStablecoin(request.asset, Chain.ETH)) {
4928
+ if (request.asset !== Asset.ETH && !isEthStablecoin(request.asset, Chain.ETH)) {
4929
+ throw new LiquidiumError(
4930
+ LiquidiumErrorCode.VALIDATION_ERROR,
4931
+ "getDepositAddress is only supported for ETH-chain assets"
4932
+ );
4933
+ }
4934
+ const selectedPool = await getPoolById(
4935
+ this.canisterContext,
4936
+ request.poolId
4937
+ );
4938
+ if (selectedPool.asset !== request.asset || selectedPool.chain !== Chain.ETH) {
4771
4939
  throw new LiquidiumError(
4772
4940
  LiquidiumErrorCode.VALIDATION_ERROR,
4773
- "getDepositAddress is only supported for ETH stablecoins"
4941
+ `Deposit address asset ${request.asset} does not match pool ${request.poolId}`
4774
4942
  );
4775
4943
  }
4776
- const tokenAddress = getEthStablecoinContractAddress(request.asset);
4777
4944
  const subaccount = encodeInflowSubaccount({
4778
4945
  action: request.action,
4779
4946
  principal: Principal.fromText(request.profileId)
@@ -4785,17 +4952,20 @@ var LendingModule = class {
4785
4952
  owner: Principal.fromText(request.poolId),
4786
4953
  subaccount: [subaccount]
4787
4954
  },
4788
- [tokenAddress]
4955
+ getEthDepositTokenAddress(request.asset)
4789
4956
  );
4790
4957
  if ("Err" in result) {
4791
4958
  throw mapDepositAccountErrorToLiquidiumError(result.Err);
4792
4959
  }
4793
- return result.Ok;
4960
+ return normalizeAndValidateEvmAddress(
4961
+ result.Ok,
4962
+ "Deposit address canister returned an invalid EVM address"
4963
+ );
4794
4964
  }
4795
4965
  /**
4796
4966
  * Estimates the network/deposit fee for an inflow target.
4797
4967
  *
4798
- * ETH stablecoin deposit-address estimates are served by the deposit-address
4968
+ * ETH-chain deposit-address estimates are served by the deposit-address
4799
4969
  * canister. BTC estimates include the ckBTC minter deposit fee and ledger fee.
4800
4970
  * ICP-chain estimates return the corresponding ICRC ledger fee.
4801
4971
  *
@@ -4809,10 +4979,10 @@ var LendingModule = class {
4809
4979
  totalFee: roundInflowFeeEstimate(request, ledgerFee)
4810
4980
  };
4811
4981
  }
4812
- if (isEthStablecoin(request.asset, request.chain)) {
4982
+ if (request.chain === Chain.ETH && (request.asset === Asset.ETH || isEthStablecoin(request.asset, request.chain))) {
4813
4983
  const result = await createDepositAccountsActor(
4814
4984
  this.canisterContext
4815
- ).estimate_deposit_fee([getEthStablecoinContractAddress(request.asset)]);
4985
+ ).estimate_deposit_fee(getEthDepositTokenAddress(request.asset));
4816
4986
  if ("Err" in result) {
4817
4987
  throw mapDepositAccountErrorToLiquidiumError(result.Err);
4818
4988
  }
@@ -4986,6 +5156,7 @@ var RATE_DECIMALS = BigInt(RATE_SCALE.toString().length - 1);
4986
5156
  // src/core/utils/asset-decimals.ts
4987
5157
  var ASSET_NATIVE_DECIMALS = {
4988
5158
  BTC: 8n,
5159
+ ETH: 18n,
4989
5160
  ICP: 8n,
4990
5161
  USDC: 6n,
4991
5162
  USDT: 6n,
@@ -5075,6 +5246,7 @@ function formatPrice(price, decimals) {
5075
5246
  var ZERO_POOL_RATE = [0n, 0n, 0n];
5076
5247
  var BACKING_POOL_CHAIN_BY_ASSET = {
5077
5248
  [Asset.BTC]: Chain.BTC,
5249
+ [Asset.ETH]: Chain.ETH,
5078
5250
  [Asset.ICP]: Chain.ICP,
5079
5251
  [Asset.USDC]: Chain.ETH,
5080
5252
  [Asset.USDT]: Chain.ETH
@@ -5965,12 +6137,14 @@ var REPAYMENT_BUFFER_SECONDS = 86400n;
5965
6137
  var RATE_SCALE2 = 10n ** 27n;
5966
6138
  var SECONDS_PER_YEAR = 31536000n;
5967
6139
  var MILLISECONDS_PER_SECOND3 = 1e3;
6140
+ var ETH_NATIVE_INFLOW_FEE_FALLBACK_WEI = 250000000000000n;
5968
6141
  var ETH_STABLECOIN_INFLOW_FEE_FALLBACK = 1500000n;
5969
6142
  var SIMPLE_LOAN_MIN_SLIPPAGE_BUFFER_BPS = 200n;
5970
6143
  var SIMPLE_LOAN_FIND_QUERY_MAX_LENGTH = 256;
5971
6144
  var SIMPLE_LOAN_WIRE_CONTEXT = "simple loan";
5972
6145
  var SIMPLE_LOAN_ASSETS = [
5973
6146
  Asset.BTC,
6147
+ Asset.ETH,
5974
6148
  Asset.ICP,
5975
6149
  Asset.USDC,
5976
6150
  Asset.USDT
@@ -6401,7 +6575,7 @@ var SimpleLoansModule = class {
6401
6575
  const target = input.targets[chain];
6402
6576
  if (!target) continue;
6403
6577
  assertTargetAsset(target, input.asset);
6404
- const inflowFee = await this.lending.estimateInflowFee(target);
6578
+ const inflowFee = await this.estimateInitialDepositInflowFee(target);
6405
6579
  targets[chain] = {
6406
6580
  amount: input.collateralAmount + inflowFee.totalFee,
6407
6581
  inflowFeeAmount: inflowFee.totalFee,
@@ -6417,6 +6591,24 @@ var SimpleLoansModule = class {
6417
6591
  expiryTimestamp: input.expiryTimestamp
6418
6592
  };
6419
6593
  }
6594
+ async estimateInitialDepositInflowFee(target) {
6595
+ try {
6596
+ const fee = await this.lending.estimateInflowFee(target);
6597
+ if (shouldUseNativeEthInflowFeeFallback(target, fee.totalFee)) {
6598
+ return {
6599
+ totalFee: getRepaymentInflowFeeFallback(target.asset, target.chain)
6600
+ };
6601
+ }
6602
+ return fee;
6603
+ } catch (error) {
6604
+ if (target.asset !== Asset.ETH || target.chain !== Chain.ETH) {
6605
+ throw error;
6606
+ }
6607
+ return {
6608
+ totalFee: getRepaymentInflowFeeFallback(target.asset, target.chain)
6609
+ };
6610
+ }
6611
+ }
6420
6612
  async resolveSimpleLoanInflowTargets(request) {
6421
6613
  const selectedPool = await getPoolById(
6422
6614
  this.canisterContext,
@@ -6467,6 +6659,12 @@ var SimpleLoansModule = class {
6467
6659
  async estimateRepaymentInflowFee(target) {
6468
6660
  try {
6469
6661
  const fee = await this.lending.estimateInflowFee(target);
6662
+ if (shouldUseNativeEthInflowFeeFallback(target, fee.totalFee)) {
6663
+ return {
6664
+ totalFee: getRepaymentInflowFeeFallback(target.asset, target.chain),
6665
+ estimateAvailable: false
6666
+ };
6667
+ }
6470
6668
  return { totalFee: fee.totalFee, estimateAvailable: true };
6471
6669
  } catch {
6472
6670
  return {
@@ -6542,11 +6740,17 @@ var SimpleLoansModule = class {
6542
6740
  }
6543
6741
  };
6544
6742
  function getRepaymentInflowFeeFallback(asset, chain) {
6743
+ if (chain === Chain.ETH && asset === Asset.ETH) {
6744
+ return ETH_NATIVE_INFLOW_FEE_FALLBACK_WEI;
6745
+ }
6545
6746
  if (chain === "ETH" && (asset === "USDT" || asset === "USDC")) {
6546
6747
  return ETH_STABLECOIN_INFLOW_FEE_FALLBACK;
6547
6748
  }
6548
6749
  return 0n;
6549
6750
  }
6751
+ function shouldUseNativeEthInflowFeeFallback(target, totalFee) {
6752
+ return totalFee <= 0n && target.asset === Asset.ETH && target.chain === Chain.ETH;
6753
+ }
6550
6754
  function assertTargetAsset(target, expectedAsset) {
6551
6755
  if (target.asset === expectedAsset) {
6552
6756
  return;
@@ -6664,6 +6868,16 @@ function validateCreateRequest(request) {
6664
6868
  "Simple loan collateral amount must be greater than zero"
6665
6869
  );
6666
6870
  }
6871
+ const collateralMinimumError = getDepositAmountMinimumValidationError({
6872
+ amount: request.collateral.amount,
6873
+ asset: request.collateral.asset
6874
+ });
6875
+ if (collateralMinimumError) {
6876
+ throw new LiquidiumError(
6877
+ LiquidiumErrorCode.VALIDATION_ERROR,
6878
+ collateralMinimumError.message
6879
+ );
6880
+ }
6667
6881
  if (request.borrow.amount <= 0n) {
6668
6882
  throw new LiquidiumError(
6669
6883
  LiquidiumErrorCode.VALIDATION_ERROR,
@@ -6696,7 +6910,7 @@ function inferSimpleLoanDeliveryChain(destination, asset) {
6696
6910
  if (asset === Asset.BTC) {
6697
6911
  return Chain.BTC;
6698
6912
  }
6699
- if (asset === Asset.USDC || asset === Asset.USDT) {
6913
+ if (asset === Asset.ETH || asset === Asset.USDC || asset === Asset.USDT) {
6700
6914
  return Chain.ETH;
6701
6915
  }
6702
6916
  return Chain.ICP;
@@ -7229,6 +7443,6 @@ function resolveEvmReadClient(config) {
7229
7443
  });
7230
7444
  }
7231
7445
 
7232
- export { AccountsModule, ActivitiesModule, ActivityFilter, Asset, CK_ETH_DEPOSIT_CONTRACT_ADDRESS, Chain, Environment, EvmSupplyApprovalStrategy, HistoryModule, LendingModule, LiquidiumAccountType, LiquidiumClient, LiquidiumError, LiquidiumErrorCode, MIN_BORROW_AMOUNTS_BY_ASSET, MIN_WITHDRAW_AMOUNTS_BY_ASSET, MarketModule, OutflowType, PositionsModule, QuoteModule, QuoteValidationErrorCode, QuoteWarningCode, RATE_DECIMALS, RATE_SCALE, SimpleLoanCreatedError, SimpleLoansModule, SupplyAction, SupplyPlanType, USDC_CONTRACT_ADDRESS, USDT_CONTRACT_ADDRESS, WalletActionKind, WalletExecutionKind, createTransferErc20Transaction, executeWith, getMinimumBorrowAmount, getMinimumWithdrawAmount, intFromPublicId, isAssetIdentifier, publicIdFromInt };
7446
+ export { AccountsModule, ActivitiesModule, ActivityFilter, Asset, CK_ETH_DEPOSIT_CONTRACT_ADDRESS, Chain, Environment, EvmSupplyApprovalStrategy, HistoryModule, LendingModule, LiquidiumAccountType, LiquidiumClient, LiquidiumError, LiquidiumErrorCode, MIN_BORROW_AMOUNTS_BY_ASSET, MIN_DEPOSIT_AMOUNTS_BY_ASSET, MIN_WITHDRAW_AMOUNTS_BY_ASSET, MarketModule, OutflowType, PositionsModule, QuoteModule, QuoteValidationErrorCode, QuoteWarningCode, RATE_DECIMALS, RATE_SCALE, SimpleLoanCreatedError, SimpleLoansModule, SupplyAction, SupplyPlanType, USDC_CONTRACT_ADDRESS, USDT_CONTRACT_ADDRESS, WalletActionKind, WalletExecutionKind, createTransferErc20Transaction, executeWith, getMinimumBorrowAmount, getMinimumDepositAmount, getMinimumWithdrawAmount, intFromPublicId, isAssetIdentifier, publicIdFromInt };
7233
7447
  //# sourceMappingURL=index.js.map
7234
7448
  //# sourceMappingURL=index.js.map