@liquidium/client 0.2.0 → 0.2.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/README.md CHANGED
@@ -14,7 +14,7 @@
14
14
  <p align="center">
15
15
  <a href="https://liquidium-inc.github.io/liquidium-sdk/"><b>SDK Docs</b></a> ·
16
16
  <a href="https://github.com/Liquidium-Inc/liquidium-sdk/tree/main/examples/instant-loans-flow"><b>Instant Loan Example</b></a> ·
17
- <a href="https://github.com/Liquidium-Inc/liquidium-sdk/tree/main/examples/vite-react-dynamic"><b>SDK Method Query Example</b></a> ·
17
+ <a href="https://github.com/Liquidium-Inc/liquidium-sdk/tree/main/examples/sdk-method-query"><b>SDK Method Query Example</b></a> ·
18
18
  <a href="#core-api"><b>Core API</b></a>
19
19
  </p>
20
20
 
@@ -148,7 +148,7 @@ Instant-loan integrations use this sequence:
148
148
  | Step | SDK call | What your app does |
149
149
  | --- | --- | --- |
150
150
  | Load market data | `client.market.listPools()` and `client.market.getAssetPrices()` | Show supported collateral and borrow assets |
151
- | Validate amounts | `client.quote.calculateLtv(...)` | Block invalid LTV or frozen-pool input before creating a loan |
151
+ | Validate amounts | `client.quote.calculateLtv(...)` | Block too-small borrow amounts, invalid LTV, or frozen-pool input before creating a loan |
152
152
  | Create loan | `client.instantLoans.create(...)` | Store `loan.ref` and show `loan.initialDeposit.amount` plus `loan.initialDeposit.target` |
153
153
  | Track loan | `client.instantLoans.get({ ref })` and `client.activities.list({ shortRef: ref })` | Reload loan state, initial deposit quote, and repayment activity |
154
154
  | Repay loan | Read `loan.repayment` | Ask the user to send `loan.repayment.amount` to `loan.repayment.target`; amount is `0n` when no repayment is due |
@@ -168,7 +168,7 @@ Creates an accountless instant loan and returns generated transfer targets.
168
168
  | `collateralAsset` | Collateral asset symbol, for example `"BTC"` |
169
169
  | `borrowAsset` | Borrow asset symbol, for example `"USDC"` |
170
170
  | `collateralAmount` | Intended credited collateral amount in base units, before deposit/inflow fees |
171
- | `borrowAmount` | Borrow amount in base units |
171
+ | `borrowAmount` | Borrow amount in base units. The SDK rejects values below the asset minimum. |
172
172
  | `ltvMaxBps` | Maximum LTV in basis points, where `6_000n` is 60% |
173
173
  | `depositWindowSeconds` | How long the user has to send collateral |
174
174
  | `borrowDestination` | External address that receives borrowed funds |
@@ -200,6 +200,18 @@ Calculates implied LTV from selected pools, prices, borrow amount, and collatera
200
200
 
201
201
  Use this before `client.instantLoans.create(...)` so your app can block invalid input and choose a safe `ltvMaxBps`.
202
202
 
203
+ ### Borrow minimums
204
+
205
+ The SDK enforces product minimums before borrow creation:
206
+
207
+ | Asset | Minimum borrow amount |
208
+ | --- | --- |
209
+ | BTC | `5_100n` sats |
210
+ | USDC | `1_000_000n` base units |
211
+ | USDT | `1_000_000n` base units |
212
+
213
+ Use `getMinimumBorrowAmount(asset)` to display the same minimum that `client.quote.calculateLtv(...)`, `client.quote.getQuote(...)`, `client.instantLoans.create(...)`, and `client.lending.prepareBorrow(...)` enforce.
214
+
203
215
  ## Response Fields
204
216
 
205
217
  Most instant-loan UIs show or store these fields:
@@ -255,7 +267,7 @@ Start browser integrations with the examples.
255
267
  | Example | What it shows |
256
268
  | --- | --- |
257
269
  | [`examples/instant-loans-flow`](https://github.com/Liquidium-Inc/liquidium-sdk/tree/main/examples/instant-loans-flow) | Accountless instant loan UX with manual destination addresses, pool selection, LTV preview, loan creation, status reload, activity status, and address recovery |
258
- | [`examples/vite-react-dynamic`](https://github.com/Liquidium-Inc/liquidium-sdk/tree/main/examples/vite-react-dynamic) | Developer tool for calling SDK methods, including instant loan method templates |
270
+ | [`examples/sdk-method-query`](https://github.com/Liquidium-Inc/liquidium-sdk/tree/main/examples/sdk-method-query) | Developer tool for calling SDK methods, including instant loan method templates |
259
271
 
260
272
  Run the instant loan example:
261
273
 
package/dist/index.cjs CHANGED
@@ -3107,6 +3107,39 @@ function assertSupportsIcrcAccountInflowTarget(asset) {
3107
3107
  );
3108
3108
  }
3109
3109
 
3110
+ // src/core/borrow-minimums.ts
3111
+ var MIN_BORROW_AMOUNTS_BY_ASSET = {
3112
+ BTC: 5100n,
3113
+ // 5,100 sats = 0.000051 BTC
3114
+ USDC: 1000000n,
3115
+ // 1 USDC
3116
+ USDT: 1000000n
3117
+ // 1 USDT
3118
+ };
3119
+ function getMinimumBorrowAmount(asset) {
3120
+ if (!isMinimumBorrowAsset(asset)) {
3121
+ return 0n;
3122
+ }
3123
+ return MIN_BORROW_AMOUNTS_BY_ASSET[asset];
3124
+ }
3125
+ function getBorrowAmountMinimumValidationError(params) {
3126
+ const minimumAmount = getMinimumBorrowAmount(params.asset);
3127
+ if (minimumAmount <= 0n || params.amount >= minimumAmount) {
3128
+ return null;
3129
+ }
3130
+ return {
3131
+ asset: params.asset,
3132
+ minimumAmount,
3133
+ message: formatMinimumBorrowAmountMessage(params.asset, minimumAmount)
3134
+ };
3135
+ }
3136
+ function formatMinimumBorrowAmountMessage(asset, minimumAmount) {
3137
+ return `Borrow amount must be at least ${minimumAmount} base units for ${asset}`;
3138
+ }
3139
+ function isMinimumBorrowAsset(asset) {
3140
+ return Object.hasOwn(MIN_BORROW_AMOUNTS_BY_ASSET, asset);
3141
+ }
3142
+
3110
3143
  // src/modules/quote/types.ts
3111
3144
  var QuoteValidationErrorCode = /* @__PURE__ */ ((QuoteValidationErrorCode2) => {
3112
3145
  QuoteValidationErrorCode2["INVALID_LTV"] = "INVALID_LTV";
@@ -3129,7 +3162,6 @@ var BASIS_POINTS_DENOMINATOR = 10000n;
3129
3162
  var BPS_PER_PERCENT = 100n;
3130
3163
  var MIN_LTV_BPS = 0n;
3131
3164
  var HIGH_LTV_WARNING_THRESHOLD_BPS = 8000n;
3132
- var MIN_BORROW_AMOUNT_BASE_UNITS = 5000n;
3133
3165
  var INTERNAL_USD_DECIMAL_PLACES = 8;
3134
3166
  var PRICE_SCALE_DECIMAL_PLACES = 8;
3135
3167
  var QuoteModule = class {
@@ -3187,11 +3219,12 @@ var QuoteModule = class {
3187
3219
  message: `Price not available for collateral asset: ${collateralPool.asset}`
3188
3220
  });
3189
3221
  }
3190
- if (request.borrowAmount <= 0n) {
3191
- validationErrors.push({
3192
- code: "BORROW_AMOUNT_TOO_LOW" /* BORROW_AMOUNT_TOO_LOW */,
3193
- message: "Borrow amount must be greater than 0"
3194
- });
3222
+ const borrowAmountError = createBorrowAmountValidationError({
3223
+ amount: request.borrowAmount,
3224
+ asset: borrowPool.asset
3225
+ });
3226
+ if (borrowAmountError) {
3227
+ validationErrors.push(borrowAmountError);
3195
3228
  }
3196
3229
  if (request.collateralAmount <= 0n) {
3197
3230
  validationErrors.push({
@@ -3313,16 +3346,12 @@ var QuoteModule = class {
3313
3346
  message: `Price not available for collateral asset: ${collateralAsset}`
3314
3347
  });
3315
3348
  }
3316
- if (borrowAmount < 0n) {
3317
- validationErrors.push({
3318
- code: "BORROW_AMOUNT_TOO_LOW" /* BORROW_AMOUNT_TOO_LOW */,
3319
- message: `Borrow amount must be non-negative`
3320
- });
3321
- } else if (borrowAmount < MIN_BORROW_AMOUNT_BASE_UNITS) {
3322
- validationErrors.push({
3323
- code: "BORROW_AMOUNT_TOO_LOW" /* BORROW_AMOUNT_TOO_LOW */,
3324
- message: `Borrow amount must be at least ${MIN_BORROW_AMOUNT_BASE_UNITS} base units`
3325
- });
3349
+ const borrowAmountError = createBorrowAmountValidationError({
3350
+ amount: borrowAmount,
3351
+ asset: borrowAsset
3352
+ });
3353
+ if (borrowAmountError) {
3354
+ validationErrors.push(borrowAmountError);
3326
3355
  }
3327
3356
  if (targetLtvBps <= MIN_LTV_BPS) {
3328
3357
  validationErrors.push({
@@ -3445,6 +3474,22 @@ function createQuoteResult(params) {
3445
3474
  warnings: params.warnings
3446
3475
  };
3447
3476
  }
3477
+ function createBorrowAmountValidationError(params) {
3478
+ if (params.amount <= 0n) {
3479
+ return {
3480
+ code: "BORROW_AMOUNT_TOO_LOW" /* BORROW_AMOUNT_TOO_LOW */,
3481
+ message: "Borrow amount must be greater than 0"
3482
+ };
3483
+ }
3484
+ const minimumBorrowAmountError = getBorrowAmountMinimumValidationError(params);
3485
+ if (!minimumBorrowAmountError) {
3486
+ return null;
3487
+ }
3488
+ return {
3489
+ code: "BORROW_AMOUNT_TOO_LOW" /* BORROW_AMOUNT_TOO_LOW */,
3490
+ message: minimumBorrowAmountError.message
3491
+ };
3492
+ }
3448
3493
  function computeUsdInternalFromBaseUnits(params) {
3449
3494
  const { amountBaseUnits, priceScaled, assetDecimalPlaces } = params;
3450
3495
  const scaleDiff = INTERNAL_USD_DECIMAL_PLACES - PRICE_SCALE_DECIMAL_PLACES;
@@ -3770,7 +3815,9 @@ var InstantLoansModule = class {
3770
3815
  );
3771
3816
  }
3772
3817
  const apiClient = this.requireApi("Instant loan address lookup");
3773
- const response = await apiClient.get(buildInstantLoanAddressLookupPath({ address: trimmedAddress }));
3818
+ const response = await apiClient.get(
3819
+ buildInstantLoanAddressLookupPath({ address: trimmedAddress })
3820
+ );
3774
3821
  return (response.candidates ?? response.loans ?? []).map(mapCandidateWire);
3775
3822
  }
3776
3823
  async mapLoanRecord(record, collateralAmount) {
@@ -3791,7 +3838,9 @@ var InstantLoansModule = class {
3791
3838
  }
3792
3839
  async getCollateralAmountHint(loanId) {
3793
3840
  const apiClient = this.requireApi("Instant loan lookup");
3794
- const response = await apiClient.get(buildInstantLoanCollateralHintPath({ loanId }));
3841
+ const response = await apiClient.get(
3842
+ buildInstantLoanCollateralHintPath({ loanId })
3843
+ );
3795
3844
  return parseBigintWire(response.collateralAmountHint, "collateral amount");
3796
3845
  }
3797
3846
  async mapLoanWire(loan) {
@@ -4715,9 +4764,28 @@ var LendingModule = class {
4715
4764
  "Borrow requires a signer account"
4716
4765
  );
4717
4766
  }
4718
- const receiverAddress = await this.normalizeOutflowReceiverAddress({
4719
- poolId: request.poolId,
4720
- receiverAddress: destinationAccount
4767
+ if (request.amount <= 0n) {
4768
+ throw new LiquidiumError(
4769
+ LiquidiumErrorCode.VALIDATION_ERROR,
4770
+ "Borrow amount must be greater than 0"
4771
+ );
4772
+ }
4773
+ const selectedPool = await this.getPoolById(request.poolId);
4774
+ const selectedAsset = getVariantKey(selectedPool.asset);
4775
+ const minimumBorrowAmountError = getBorrowAmountMinimumValidationError({
4776
+ amount: request.amount,
4777
+ asset: selectedAsset
4778
+ });
4779
+ if (minimumBorrowAmountError) {
4780
+ throw new LiquidiumError(
4781
+ LiquidiumErrorCode.VALIDATION_ERROR,
4782
+ minimumBorrowAmountError.message
4783
+ );
4784
+ }
4785
+ const receiverAddress = normalizeExternalAddress({
4786
+ address: destinationAccount,
4787
+ asset: selectedAsset,
4788
+ chain: getVariantKey(selectedPool.chain)
4721
4789
  });
4722
4790
  const lendingActor = createLendingActor(this.canisterContext);
4723
4791
  try {
@@ -5985,6 +6053,7 @@ exports.LendingModule = LendingModule;
5985
6053
  exports.LiquidiumClient = LiquidiumClient;
5986
6054
  exports.LiquidiumError = LiquidiumError;
5987
6055
  exports.LiquidiumErrorCode = LiquidiumErrorCode;
6056
+ exports.MIN_BORROW_AMOUNTS_BY_ASSET = MIN_BORROW_AMOUNTS_BY_ASSET;
5988
6057
  exports.MarketModule = MarketModule;
5989
6058
  exports.OutflowType = OutflowType;
5990
6059
  exports.PositionsModule = PositionsModule;
@@ -6002,6 +6071,7 @@ exports.UserHistoryStatus = UserHistoryStatus;
6002
6071
  exports.WalletExecutionKind = WalletExecutionKind;
6003
6072
  exports.createTransferErc20Transaction = createTransferErc20Transaction;
6004
6073
  exports.executeWith = executeWith;
6074
+ exports.getMinimumBorrowAmount = getMinimumBorrowAmount;
6005
6075
  exports.intFromPublicId = intFromPublicId;
6006
6076
  exports.publicIdFromInt = publicIdFromInt;
6007
6077
  //# sourceMappingURL=index.cjs.map