@liquidium/client 0.2.0 → 0.3.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.js CHANGED
@@ -1591,9 +1591,9 @@ function buildActivityStatusPath(request) {
1591
1591
  request.id
1592
1592
  )}/status?${query.toString()}`;
1593
1593
  }
1594
- function buildInstantLoanAddressLookupPath(request) {
1595
- const query = new URLSearchParams({ address: request.address });
1596
- return `${INSTANT_LOANS}/address?${query.toString()}`;
1594
+ function buildInstantLoanFindPath(request) {
1595
+ const query = new URLSearchParams({ query: request.query });
1596
+ return `${INSTANT_LOANS}/find?${query.toString()}`;
1597
1597
  }
1598
1598
  function buildInstantLoanCollateralHintPath(request) {
1599
1599
  return `${INSTANT_LOANS}/${encodeURIComponent(
@@ -2222,6 +2222,41 @@ function mapHistoryStatusToApi(status) {
2222
2222
  return status.toUpperCase();
2223
2223
  }
2224
2224
 
2225
+ // src/core/utils/api-response-parsers.ts
2226
+ var MILLISECONDS_PER_SECOND2 = 1e3;
2227
+ function parseNonEmptyApiString(value, field) {
2228
+ if (!value) {
2229
+ throwInvalidApiResponseField(field);
2230
+ }
2231
+ return value;
2232
+ }
2233
+ function parseUnsignedApiBigint(value, field) {
2234
+ if (!value || !/^\d+$/.test(value)) {
2235
+ throwInvalidApiResponseField(field);
2236
+ }
2237
+ return BigInt(value);
2238
+ }
2239
+ function parseIsoApiTimestampToUnixSeconds(value, field) {
2240
+ const timestamp = parseNonEmptyApiString(value, field);
2241
+ const timestampMilliseconds = Date.parse(timestamp);
2242
+ if (!Number.isFinite(timestampMilliseconds)) {
2243
+ throwInvalidApiResponseField(field);
2244
+ }
2245
+ return BigInt(Math.floor(timestampMilliseconds / MILLISECONDS_PER_SECOND2));
2246
+ }
2247
+ function parseApiStringUnion(value, allowedValues, field) {
2248
+ if (value !== void 0 && allowedValues.includes(value)) {
2249
+ return value;
2250
+ }
2251
+ throwInvalidApiResponseField(field);
2252
+ }
2253
+ function throwInvalidApiResponseField(field) {
2254
+ throw new LiquidiumError(
2255
+ LiquidiumErrorCode.VALIDATION_ERROR,
2256
+ `Invalid ${field.context} ${field.label}`
2257
+ );
2258
+ }
2259
+
2225
2260
  // src/core/utils/asset-decimals.ts
2226
2261
  var ASSET_NATIVE_DECIMALS = {
2227
2262
  BTC: 8n,
@@ -3105,6 +3140,39 @@ function assertSupportsIcrcAccountInflowTarget(asset) {
3105
3140
  );
3106
3141
  }
3107
3142
 
3143
+ // src/core/borrow-minimums.ts
3144
+ var MIN_BORROW_AMOUNTS_BY_ASSET = {
3145
+ BTC: 5100n,
3146
+ // 5,100 sats = 0.000051 BTC
3147
+ USDC: 1000000n,
3148
+ // 1 USDC
3149
+ USDT: 1000000n
3150
+ // 1 USDT
3151
+ };
3152
+ function getMinimumBorrowAmount(asset) {
3153
+ if (!isMinimumBorrowAsset(asset)) {
3154
+ return 0n;
3155
+ }
3156
+ return MIN_BORROW_AMOUNTS_BY_ASSET[asset];
3157
+ }
3158
+ function getBorrowAmountMinimumValidationError(params) {
3159
+ const minimumAmount = getMinimumBorrowAmount(params.asset);
3160
+ if (minimumAmount <= 0n || params.amount >= minimumAmount) {
3161
+ return null;
3162
+ }
3163
+ return {
3164
+ asset: params.asset,
3165
+ minimumAmount,
3166
+ message: formatMinimumBorrowAmountMessage(params.asset, minimumAmount)
3167
+ };
3168
+ }
3169
+ function formatMinimumBorrowAmountMessage(asset, minimumAmount) {
3170
+ return `Borrow amount must be at least ${minimumAmount} base units for ${asset}`;
3171
+ }
3172
+ function isMinimumBorrowAsset(asset) {
3173
+ return Object.hasOwn(MIN_BORROW_AMOUNTS_BY_ASSET, asset);
3174
+ }
3175
+
3108
3176
  // src/modules/quote/types.ts
3109
3177
  var QuoteValidationErrorCode = /* @__PURE__ */ ((QuoteValidationErrorCode2) => {
3110
3178
  QuoteValidationErrorCode2["INVALID_LTV"] = "INVALID_LTV";
@@ -3127,7 +3195,6 @@ var BASIS_POINTS_DENOMINATOR = 10000n;
3127
3195
  var BPS_PER_PERCENT = 100n;
3128
3196
  var MIN_LTV_BPS = 0n;
3129
3197
  var HIGH_LTV_WARNING_THRESHOLD_BPS = 8000n;
3130
- var MIN_BORROW_AMOUNT_BASE_UNITS = 5000n;
3131
3198
  var INTERNAL_USD_DECIMAL_PLACES = 8;
3132
3199
  var PRICE_SCALE_DECIMAL_PLACES = 8;
3133
3200
  var QuoteModule = class {
@@ -3185,11 +3252,12 @@ var QuoteModule = class {
3185
3252
  message: `Price not available for collateral asset: ${collateralPool.asset}`
3186
3253
  });
3187
3254
  }
3188
- if (request.borrowAmount <= 0n) {
3189
- validationErrors.push({
3190
- code: "BORROW_AMOUNT_TOO_LOW" /* BORROW_AMOUNT_TOO_LOW */,
3191
- message: "Borrow amount must be greater than 0"
3192
- });
3255
+ const borrowAmountError = createBorrowAmountValidationError({
3256
+ amount: request.borrowAmount,
3257
+ asset: borrowPool.asset
3258
+ });
3259
+ if (borrowAmountError) {
3260
+ validationErrors.push(borrowAmountError);
3193
3261
  }
3194
3262
  if (request.collateralAmount <= 0n) {
3195
3263
  validationErrors.push({
@@ -3311,16 +3379,12 @@ var QuoteModule = class {
3311
3379
  message: `Price not available for collateral asset: ${collateralAsset}`
3312
3380
  });
3313
3381
  }
3314
- if (borrowAmount < 0n) {
3315
- validationErrors.push({
3316
- code: "BORROW_AMOUNT_TOO_LOW" /* BORROW_AMOUNT_TOO_LOW */,
3317
- message: `Borrow amount must be non-negative`
3318
- });
3319
- } else if (borrowAmount < MIN_BORROW_AMOUNT_BASE_UNITS) {
3320
- validationErrors.push({
3321
- code: "BORROW_AMOUNT_TOO_LOW" /* BORROW_AMOUNT_TOO_LOW */,
3322
- message: `Borrow amount must be at least ${MIN_BORROW_AMOUNT_BASE_UNITS} base units`
3323
- });
3382
+ const borrowAmountError = createBorrowAmountValidationError({
3383
+ amount: borrowAmount,
3384
+ asset: borrowAsset
3385
+ });
3386
+ if (borrowAmountError) {
3387
+ validationErrors.push(borrowAmountError);
3324
3388
  }
3325
3389
  if (targetLtvBps <= MIN_LTV_BPS) {
3326
3390
  validationErrors.push({
@@ -3443,6 +3507,22 @@ function createQuoteResult(params) {
3443
3507
  warnings: params.warnings
3444
3508
  };
3445
3509
  }
3510
+ function createBorrowAmountValidationError(params) {
3511
+ if (params.amount <= 0n) {
3512
+ return {
3513
+ code: "BORROW_AMOUNT_TOO_LOW" /* BORROW_AMOUNT_TOO_LOW */,
3514
+ message: "Borrow amount must be greater than 0"
3515
+ };
3516
+ }
3517
+ const minimumBorrowAmountError = getBorrowAmountMinimumValidationError(params);
3518
+ if (!minimumBorrowAmountError) {
3519
+ return null;
3520
+ }
3521
+ return {
3522
+ code: "BORROW_AMOUNT_TOO_LOW" /* BORROW_AMOUNT_TOO_LOW */,
3523
+ message: minimumBorrowAmountError.message
3524
+ };
3525
+ }
3446
3526
  function computeUsdInternalFromBaseUnits(params) {
3447
3527
  const { amountBaseUnits, priceScaled, assetDecimalPlaces } = params;
3448
3528
  const scaleDiff = INTERNAL_USD_DECIMAL_PLACES - PRICE_SCALE_DECIMAL_PLACES;
@@ -3568,6 +3648,14 @@ var RATE_SCALE2 = 10n ** 27n;
3568
3648
  var SECONDS_PER_YEAR = 31536000n;
3569
3649
  var ETH_STABLECOIN_INFLOW_FEE_FALLBACK = 1500000n;
3570
3650
  var INSTANT_LOAN_MIN_SLIPPAGE_BUFFER_BPS = 200n;
3651
+ var INSTANT_LOAN_FIND_QUERY_MAX_LENGTH = 256;
3652
+ var INSTANT_LOAN_WIRE_CONTEXT = "instant loan";
3653
+ var INSTANT_LOAN_ASSETS = [
3654
+ Asset.BTC,
3655
+ Asset.SOL,
3656
+ Asset.USDC,
3657
+ Asset.USDT
3658
+ ];
3571
3659
  var InstantLoansModule = class {
3572
3660
  constructor(canisterContext, apiClient, lending, positions) {
3573
3661
  this.canisterContext = canisterContext;
@@ -3624,7 +3712,19 @@ var InstantLoansModule = class {
3624
3712
  borrowDestination,
3625
3713
  refundDestination
3626
3714
  });
3627
- return await this.mapLoanWire(response.loan);
3715
+ const loanId = parseUnsignedApiBigint(response.loan.loanId, {
3716
+ context: INSTANT_LOAN_WIRE_CONTEXT,
3717
+ label: "loan ID"
3718
+ });
3719
+ const collateralAmount = parseUnsignedApiBigint(
3720
+ response.loan.collateral.amountHint,
3721
+ {
3722
+ context: INSTANT_LOAN_WIRE_CONTEXT,
3723
+ label: "collateral amount"
3724
+ }
3725
+ );
3726
+ const record = await this.getLoanRecord(loanId);
3727
+ return await this.mapLoanRecord(record, collateralAmount);
3628
3728
  }
3629
3729
  /**
3630
3730
  * Resolves canonical canister state by loan id or short reference.
@@ -3637,21 +3737,30 @@ var InstantLoansModule = class {
3637
3737
  */
3638
3738
  async get(request) {
3639
3739
  const loanId = "loanId" in request ? request.loanId : decodeRef(request.ref);
3640
- try {
3641
- const result = await createInstantLoansActor(
3642
- this.canisterContext
3643
- ).get_loan(loanId);
3644
- if ("Err" in result) {
3645
- throw mapInstantLoansErrorToLiquidiumError(result.Err);
3646
- }
3647
- const collateralAmount = await this.getCollateralAmountHint(loanId);
3648
- return await this.mapLoanRecord(result.Ok, collateralAmount);
3649
- } catch (error) {
3650
- if (error instanceof LiquidiumError) {
3651
- throw error;
3652
- }
3653
- throw mapCanisterCallErrorToLiquidiumError("get_loan", error);
3654
- }
3740
+ const record = await this.getLoanRecord(loanId);
3741
+ const collateralAmount = await this.getCollateralAmountHint(loanId);
3742
+ return await this.mapLoanRecord(record, collateralAmount);
3743
+ }
3744
+ /**
3745
+ * Finds instant loans by short reference, numeric loan id string, address, or transaction id.
3746
+ *
3747
+ * Search returns lightweight matches. Call `get({ loanId })` or `get({ ref })`
3748
+ * when the user selects a match and you need hydrated loan state.
3749
+ *
3750
+ * @param query - Short reference, address, transaction id/hash, or numeric loan id string.
3751
+ * @returns Matching loan ids and references from the search index.
3752
+ */
3753
+ async find(query) {
3754
+ const validatedQuery = validateInstantLoanFindQuery(query);
3755
+ const candidates = await this.findCandidateLoansByQuery(validatedQuery);
3756
+ return uniqueInstantLoanFindCandidates(candidates).map((candidate) => ({
3757
+ loanId: candidate.loanId,
3758
+ ref: candidate.ref,
3759
+ createdAt: candidate.createdAt,
3760
+ collateral: candidate.collateral,
3761
+ borrow: candidate.borrow,
3762
+ profileId: candidate.profileId
3763
+ }));
3655
3764
  }
3656
3765
  /**
3657
3766
  * Returns the active instant-loans canister config via direct query.
@@ -3749,28 +3858,29 @@ var InstantLoansModule = class {
3749
3858
  throw mapCanisterCallErrorToLiquidiumError("list_warmed_profiles", error);
3750
3859
  }
3751
3860
  }
3752
- /**
3753
- * Finds candidate loans associated with an address through the Liquidium SDK
3754
- * API. Returns discovery candidates only; call `get(...)` to hydrate canister state.
3755
- *
3756
- * Candidates are useful for recovery flows where the user knows a borrow or
3757
- * refund address but not the loan reference.
3758
- *
3759
- * @param address - Borrow or refund address to search for.
3760
- * @returns Lightweight loan candidates associated with the address.
3761
- */
3762
- async findByAddress(address) {
3763
- const trimmedAddress = address.trim();
3764
- if (!trimmedAddress) {
3765
- throw new LiquidiumError(
3766
- LiquidiumErrorCode.VALIDATION_ERROR,
3767
- "Address lookup requires a non-empty address"
3768
- );
3769
- }
3770
- const apiClient = this.requireApi("Instant loan address lookup");
3771
- const response = await apiClient.get(buildInstantLoanAddressLookupPath({ address: trimmedAddress }));
3861
+ async findCandidateLoansByQuery(query) {
3862
+ const apiClient = this.requireApi("Instant loan find");
3863
+ const response = await apiClient.get(
3864
+ buildInstantLoanFindPath({ query })
3865
+ );
3772
3866
  return (response.candidates ?? response.loans ?? []).map(mapCandidateWire);
3773
3867
  }
3868
+ async getLoanRecord(loanId) {
3869
+ try {
3870
+ const result = await createInstantLoansActor(
3871
+ this.canisterContext
3872
+ ).get_loan(loanId);
3873
+ if ("Err" in result) {
3874
+ throw mapInstantLoansErrorToLiquidiumError(result.Err);
3875
+ }
3876
+ return result.Ok;
3877
+ } catch (error) {
3878
+ if (error instanceof LiquidiumError) {
3879
+ throw error;
3880
+ }
3881
+ throw mapCanisterCallErrorToLiquidiumError("get_loan", error);
3882
+ }
3883
+ }
3774
3884
  async mapLoanRecord(record, collateralAmount) {
3775
3885
  return await this.hydrateLoan({
3776
3886
  loanId: record.id,
@@ -3784,35 +3894,22 @@ var InstantLoansModule = class {
3784
3894
  borrowAsset: getVariantKey(record.borrow_asset),
3785
3895
  borrowAmount: record.borrow_amount,
3786
3896
  borrowDestination: accountFromCanister(record.borrow_destination),
3787
- refundDestination: accountFromCanister(record.refund_destination)
3897
+ refundDestination: accountFromCanister(record.refund_destination),
3898
+ depositDetectedTimestamp: record.deposit_detected_ts[0] ?? null,
3899
+ expiryTimestamp: record.expires_at[0] ?? deriveDepositExpiryTimestamp({
3900
+ depositDetectedTimestamp: record.deposit_detected_ts[0] ?? null,
3901
+ depositWindowSeconds: record.ltv_timer_s
3902
+ })
3788
3903
  });
3789
3904
  }
3790
3905
  async getCollateralAmountHint(loanId) {
3791
- const apiClient = this.requireApi("Instant loan lookup");
3792
- const response = await apiClient.get(buildInstantLoanCollateralHintPath({ loanId }));
3793
- return parseBigintWire(response.collateralAmountHint, "collateral amount");
3794
- }
3795
- async mapLoanWire(loan) {
3796
- const loanId = parseBigintWire(loan.loanId, "loan ID");
3797
- return await this.hydrateLoan({
3798
- loanId,
3799
- profileId: loan.profileId,
3800
- ltvMaxBps: parseBigintWire(loan.ltvMaxBps, "max LTV"),
3801
- depositWindowSeconds: parseBigintWire(
3802
- loan.depositWindowSeconds,
3803
- "deposit window"
3804
- ),
3805
- collateralPoolId: loan.collateral.poolId,
3806
- collateralAmount: parseBigintWire(
3807
- loan.collateral.amountHint,
3808
- "collateral amount"
3809
- ),
3810
- borrowPoolId: loan.borrow.poolId,
3811
- collateralAsset: loan.collateral.asset,
3812
- borrowAsset: loan.borrow.asset,
3813
- borrowAmount: parseBigintWire(loan.borrow.amount, "borrow amount"),
3814
- borrowDestination: accountFromWire(loan.borrow.destination),
3815
- refundDestination: accountFromWire(loan.refundDestination)
3906
+ const apiClient = this.requireApi("Instant loan collateral hint");
3907
+ const response = await apiClient.get(
3908
+ buildInstantLoanCollateralHintPath({ loanId })
3909
+ );
3910
+ return parseUnsignedApiBigint(response.collateralAmountHint, {
3911
+ context: INSTANT_LOAN_WIRE_CONTEXT,
3912
+ label: "collateral amount"
3816
3913
  });
3817
3914
  }
3818
3915
  async hydrateLoan(input) {
@@ -3866,7 +3963,9 @@ var InstantLoansModule = class {
3866
3963
  collateralAmount,
3867
3964
  decimals: collateralDecimals,
3868
3965
  asset: collateralAsset,
3869
- target: depositTarget
3966
+ target: depositTarget,
3967
+ detectedTimestamp: input.depositDetectedTimestamp,
3968
+ expiryTimestamp: input.expiryTimestamp
3870
3969
  });
3871
3970
  const repayment = {
3872
3971
  amount: repaymentAmount,
@@ -3930,7 +4029,9 @@ var InstantLoansModule = class {
3930
4029
  inflowFeeAmount: inflowFee.totalFee,
3931
4030
  asset: input.asset,
3932
4031
  chain: input.target.chain,
3933
- target: input.target
4032
+ target: input.target,
4033
+ detectedTimestamp: input.detectedTimestamp,
4034
+ expiryTimestamp: input.expiryTimestamp
3934
4035
  };
3935
4036
  }
3936
4037
  async estimateRepaymentInflowFee(asset, chain) {
@@ -4026,6 +4127,12 @@ function deriveInstantLoanStatus(input) {
4026
4127
  }
4027
4128
  return InstantLoanStatus.awaitingDeposit;
4028
4129
  }
4130
+ function deriveDepositExpiryTimestamp(input) {
4131
+ if (input.depositDetectedTimestamp === null) {
4132
+ return null;
4133
+ }
4134
+ return input.depositDetectedTimestamp + input.depositWindowSeconds;
4135
+ }
4029
4136
  function validateCreateRequest(request) {
4030
4137
  if (request.collateralAmount <= 0n) {
4031
4138
  throw new LiquidiumError(
@@ -4074,18 +4181,6 @@ function accountFromCanister(account) {
4074
4181
  }
4075
4182
  return { type: "External", address: account.External };
4076
4183
  }
4077
- function accountFromWire(account) {
4078
- if ("Native" in account) {
4079
- return { type: "Native", principal: account.Native };
4080
- }
4081
- if ("External" in account) {
4082
- return { type: "External", address: account.External };
4083
- }
4084
- if (account.type === "Native") {
4085
- return { type: "Native", principal: account.principal };
4086
- }
4087
- return { type: "External", address: account.address };
4088
- }
4089
4184
  function mapInstantLoanEvent(event) {
4090
4185
  return {
4091
4186
  id: event.id,
@@ -4204,55 +4299,80 @@ function decodeRef(ref) {
4204
4299
  );
4205
4300
  }
4206
4301
  }
4207
- function mapCandidateWire(wire) {
4208
- const loanId = parseBigintWire(wire.loanId ?? wire.loan_id, "loan ID");
4209
- const ref = wire.ref ?? wire.short_ref ?? publicIdFromInt(loanId);
4210
- const createdAt = wire.createdAt ?? wire.created_at;
4211
- return {
4212
- loanId,
4213
- ref,
4214
- profileId: requiredString(
4215
- wire.profileId ?? wire.lending_profile,
4216
- "profile ID"
4217
- ),
4218
- ...createdAt ? { createdAt: new Date(createdAt) } : {},
4219
- collateralPoolId: requiredString(
4220
- wire.collateralPoolId ?? wire.lend_pool_ic_id,
4221
- "collateral pool ID"
4222
- ),
4223
- borrowPoolId: requiredString(
4224
- wire.borrowPoolId ?? wire.borrow_pool_ic_id,
4225
- "borrow pool ID"
4226
- ),
4227
- collateralAsset: requiredString(
4228
- wire.collateralAsset ?? wire.lend_asset,
4229
- "collateral asset"
4230
- ),
4231
- borrowAsset: requiredString(
4232
- wire.borrowAsset ?? wire.borrow_asset,
4233
- "borrow asset"
4234
- ),
4235
- collateralAmount: parseBigintWire(
4236
- wire.collateralAmount,
4237
- "collateral amount"
4238
- )
4239
- };
4240
- }
4241
- function parseBigintWire(value, label) {
4242
- if (!value || !/^\d+$/.test(value)) {
4302
+ function validateInstantLoanFindQuery(query) {
4303
+ if (typeof query !== "string") {
4243
4304
  throw new LiquidiumError(
4244
4305
  LiquidiumErrorCode.VALIDATION_ERROR,
4245
- `Invalid instant loan ${label}`
4306
+ "Instant loan find query must be a string"
4246
4307
  );
4247
4308
  }
4248
- return BigInt(value);
4309
+ const trimmedQuery = query.trim();
4310
+ if (!trimmedQuery) {
4311
+ throw new LiquidiumError(
4312
+ LiquidiumErrorCode.VALIDATION_ERROR,
4313
+ "Instant loan find query must be non-empty"
4314
+ );
4315
+ }
4316
+ if (trimmedQuery.length > INSTANT_LOAN_FIND_QUERY_MAX_LENGTH) {
4317
+ throw new LiquidiumError(
4318
+ LiquidiumErrorCode.VALIDATION_ERROR,
4319
+ `Instant loan find query must be at most ${INSTANT_LOAN_FIND_QUERY_MAX_LENGTH.toString()} characters`
4320
+ );
4321
+ }
4322
+ return trimmedQuery;
4249
4323
  }
4250
- function requiredString(value, label) {
4251
- if (value?.trim()) return value;
4252
- throw new LiquidiumError(
4253
- LiquidiumErrorCode.VALIDATION_ERROR,
4254
- `Missing instant loan ${label}`
4255
- );
4324
+ function uniqueInstantLoanFindCandidates(candidates) {
4325
+ const candidatesByLoanId = /* @__PURE__ */ new Map();
4326
+ for (const candidate of candidates) {
4327
+ if (!candidatesByLoanId.has(candidate.loanId)) {
4328
+ candidatesByLoanId.set(candidate.loanId, candidate);
4329
+ }
4330
+ }
4331
+ return [...candidatesByLoanId.values()];
4332
+ }
4333
+ function mapCandidateWire(wire) {
4334
+ return {
4335
+ loanId: parseUnsignedApiBigint(wire.loan_id, {
4336
+ context: INSTANT_LOAN_WIRE_CONTEXT,
4337
+ label: "loan ID"
4338
+ }),
4339
+ ref: parseNonEmptyApiString(wire.short_ref, {
4340
+ context: INSTANT_LOAN_WIRE_CONTEXT,
4341
+ label: "short reference"
4342
+ }),
4343
+ createdAt: parseIsoApiTimestampToUnixSeconds(wire.created_at, {
4344
+ context: INSTANT_LOAN_WIRE_CONTEXT,
4345
+ label: "creation timestamp"
4346
+ }),
4347
+ collateral: {
4348
+ poolId: parseNonEmptyApiString(wire.lend_pool_ic_id, {
4349
+ context: INSTANT_LOAN_WIRE_CONTEXT,
4350
+ label: "lend pool ID"
4351
+ }),
4352
+ asset: parseApiStringUnion(wire.lend_asset, INSTANT_LOAN_ASSETS, {
4353
+ context: INSTANT_LOAN_WIRE_CONTEXT,
4354
+ label: "lend asset"
4355
+ }),
4356
+ amount: parseUnsignedApiBigint(wire.collateral_amount, {
4357
+ context: INSTANT_LOAN_WIRE_CONTEXT,
4358
+ label: "collateral amount"
4359
+ })
4360
+ },
4361
+ borrow: {
4362
+ poolId: parseNonEmptyApiString(wire.borrow_pool_ic_id, {
4363
+ context: INSTANT_LOAN_WIRE_CONTEXT,
4364
+ label: "borrow pool ID"
4365
+ }),
4366
+ asset: parseApiStringUnion(wire.borrow_asset, INSTANT_LOAN_ASSETS, {
4367
+ context: INSTANT_LOAN_WIRE_CONTEXT,
4368
+ label: "borrow asset"
4369
+ })
4370
+ },
4371
+ profileId: parseNonEmptyApiString(wire.profile, {
4372
+ context: INSTANT_LOAN_WIRE_CONTEXT,
4373
+ label: "profile ID"
4374
+ })
4375
+ };
4256
4376
  }
4257
4377
  function mapInstantLoansErrorToLiquidiumError(error) {
4258
4378
  const [key, payload] = Object.entries(error)[0];
@@ -4713,9 +4833,28 @@ var LendingModule = class {
4713
4833
  "Borrow requires a signer account"
4714
4834
  );
4715
4835
  }
4716
- const receiverAddress = await this.normalizeOutflowReceiverAddress({
4717
- poolId: request.poolId,
4718
- receiverAddress: destinationAccount
4836
+ if (request.amount <= 0n) {
4837
+ throw new LiquidiumError(
4838
+ LiquidiumErrorCode.VALIDATION_ERROR,
4839
+ "Borrow amount must be greater than 0"
4840
+ );
4841
+ }
4842
+ const selectedPool = await this.getPoolById(request.poolId);
4843
+ const selectedAsset = getVariantKey(selectedPool.asset);
4844
+ const minimumBorrowAmountError = getBorrowAmountMinimumValidationError({
4845
+ amount: request.amount,
4846
+ asset: selectedAsset
4847
+ });
4848
+ if (minimumBorrowAmountError) {
4849
+ throw new LiquidiumError(
4850
+ LiquidiumErrorCode.VALIDATION_ERROR,
4851
+ minimumBorrowAmountError.message
4852
+ );
4853
+ }
4854
+ const receiverAddress = normalizeExternalAddress({
4855
+ address: destinationAccount,
4856
+ asset: selectedAsset,
4857
+ chain: getVariantKey(selectedPool.chain)
4719
4858
  });
4720
4859
  const lendingActor = createLendingActor(this.canisterContext);
4721
4860
  try {
@@ -5838,7 +5977,7 @@ var PositionsModule = class {
5838
5977
  pool,
5839
5978
  priceUsd,
5840
5979
  suppliedUsd: nativeAmountToUsdScaled(
5841
- position.deposited + position.earnedInterest,
5980
+ position.deposited,
5842
5981
  position.depositedDecimals,
5843
5982
  priceUsd
5844
5983
  ),
@@ -5873,6 +6012,24 @@ var PositionsModule = class {
5873
6012
  const buffered = rawDebt * (BPS_SCALE + bufferBps) / BPS_SCALE;
5874
6013
  return { amount: buffered, decimals: position.borrowedDecimals };
5875
6014
  }
6015
+ /**
6016
+ * Returns the current full withdraw amount for a position.
6017
+ *
6018
+ * `Position.deposited` already reflects the current supplied balance at the
6019
+ * latest lending index; do not add `earnedInterest` to this amount.
6020
+ * Pass `amount` to withdraw calls and use `decimals` for display formatting.
6021
+ *
6022
+ * @param profileId - The Liquidium profile principal text.
6023
+ * @param poolId - The pool principal text.
6024
+ * @returns Full withdraw amount in the supplied asset's base units.
6025
+ */
6026
+ async getFullWithdrawAmount(profileId, poolId) {
6027
+ const position = await this.getPosition(profileId, poolId);
6028
+ if (!position) {
6029
+ return { amount: 0n, decimals: 0n };
6030
+ }
6031
+ return { amount: position.deposited, decimals: position.depositedDecimals };
6032
+ }
5876
6033
  };
5877
6034
  function nativeAmountToUsdScaled(amount, nativeDecimals, priceUsd) {
5878
6035
  if (amount <= 0n || priceUsd <= 0) {
@@ -5964,6 +6121,6 @@ function resolveEvmReadClient(config) {
5964
6121
  });
5965
6122
  }
5966
6123
 
5967
- export { AccountsModule, ActivitiesModule, ActivityDirection, ActivityFilter, ActivityKind, ActivityStatus, Asset, CK_ETH_DEPOSIT_CONTRACT_ADDRESS, Chain, Environment, EvmSupplyApprovalStrategy, HistoryModule, InflowSubmitType, InstantLoanStatus, InstantLoansModule, LendingModule, LiquidiumClient, LiquidiumError, LiquidiumErrorCode, MarketModule, OutflowType, PositionsModule, QuoteModule, QuoteValidationErrorCode, QuoteWarningCode, RATE_DECIMALS, RATE_SCALE, SupplyAction, SupplyPlanType, TransferMode, USDC_CONTRACT_ADDRESS, USDT_CONTRACT_ADDRESS, UserHistoryStatus, WalletExecutionKind, createTransferErc20Transaction, executeWith, intFromPublicId, publicIdFromInt };
6124
+ export { AccountsModule, ActivitiesModule, ActivityDirection, ActivityFilter, ActivityKind, ActivityStatus, Asset, CK_ETH_DEPOSIT_CONTRACT_ADDRESS, Chain, Environment, EvmSupplyApprovalStrategy, HistoryModule, InflowSubmitType, InstantLoanStatus, InstantLoansModule, LendingModule, LiquidiumClient, LiquidiumError, LiquidiumErrorCode, MIN_BORROW_AMOUNTS_BY_ASSET, MarketModule, OutflowType, PositionsModule, QuoteModule, QuoteValidationErrorCode, QuoteWarningCode, RATE_DECIMALS, RATE_SCALE, SupplyAction, SupplyPlanType, TransferMode, USDC_CONTRACT_ADDRESS, USDT_CONTRACT_ADDRESS, UserHistoryStatus, WalletExecutionKind, createTransferErc20Transaction, executeWith, getMinimumBorrowAmount, intFromPublicId, publicIdFromInt };
5968
6125
  //# sourceMappingURL=index.js.map
5969
6126
  //# sourceMappingURL=index.js.map