@liquidium/client 0.2.1 → 0.3.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
@@ -2,6 +2,7 @@ import { encodeFunctionData, createPublicClient, http, isAddress, getAddress } f
2
2
  import { mainnet } from 'viem/chains';
3
3
  import { Actor, HttpAgent } from '@icp-sdk/core/agent';
4
4
  import { Principal } from '@icp-sdk/core/principal';
5
+ import { idlLabelToId } from '@icp-sdk/core/candid';
5
6
  import { encodeIcrcAccount } from '@dfinity/ledger-icrc';
6
7
  import { Principal as Principal$1 } from '@dfinity/principal';
7
8
  import { validate, Network } from 'bitcoin-address-validation';
@@ -1305,17 +1306,35 @@ function mapCanisterWalletToWallet(canisterWallet) {
1305
1306
  );
1306
1307
  }
1307
1308
  }
1308
- var idlFactory2 = ({ IDL }) => {
1309
+ var KNOWN_ASSET_TAGS = ["BTC", "SOL", "USDC", "USDT"];
1310
+ var KNOWN_CHAIN_TAGS = ["BTC", "ETH", "SOL"];
1311
+ function extractVariantTag(variant, knownTags) {
1312
+ const [key] = Object.keys(variant);
1313
+ if (!key) {
1314
+ return null;
1315
+ }
1316
+ if (knownTags.includes(key)) {
1317
+ return key;
1318
+ }
1319
+ const hashMatch = /^_(\d+)_$/.exec(key);
1320
+ if (!hashMatch) {
1321
+ return null;
1322
+ }
1323
+ const hash = Number(hashMatch[1]);
1324
+ for (const tag of knownTags) {
1325
+ if (idlLabelToId(tag) === hash) {
1326
+ return tag;
1327
+ }
1328
+ }
1329
+ return null;
1330
+ }
1331
+
1332
+ // src/core/canisters/instant-loans/flexible-actor.ts
1333
+ var flexibleInstantLoansIdlFactory = ({ IDL }) => {
1309
1334
  const AccountType = IDL.Variant({
1310
1335
  Native: IDL.Principal,
1311
1336
  External: IDL.Text
1312
1337
  });
1313
- const Assets = IDL.Variant({
1314
- BTC: IDL.Null,
1315
- SOL: IDL.Null,
1316
- USDC: IDL.Null,
1317
- USDT: IDL.Null
1318
- });
1319
1338
  const SignatureVerificationError = IDL.Variant({
1320
1339
  InvalidEthSignature: IDL.Null,
1321
1340
  UnsupportedChain: IDL.Null,
@@ -1386,13 +1405,13 @@ var idlFactory2 = ({ IDL }) => {
1386
1405
  });
1387
1406
  const CreateLoanRequest = IDL.Record({
1388
1407
  borrow_destination: AccountType,
1389
- lend_asset: Assets,
1408
+ lend_asset: IDL.Unknown,
1390
1409
  borrow_amount: IDL.Nat,
1391
1410
  lend_pool_id: IDL.Principal,
1392
1411
  refund_destination: AccountType,
1393
1412
  ltv_max_bps: IDL.Nat64,
1394
1413
  borrow_pool_id: IDL.Principal,
1395
- borrow_asset: Assets,
1414
+ borrow_asset: IDL.Unknown,
1396
1415
  ltv_timer_s: IDL.Nat64
1397
1416
  });
1398
1417
  const CreateLoanResponse = IDL.Record({
@@ -1408,7 +1427,7 @@ var idlFactory2 = ({ IDL }) => {
1408
1427
  LoanCreated: IDL.Record({
1409
1428
  loan_id: IDL.Nat,
1410
1429
  borrow_destination: AccountType,
1411
- lend_asset: Assets,
1430
+ lend_asset: IDL.Unknown,
1412
1431
  borrow_amount: IDL.Nat,
1413
1432
  lend_pool_id: IDL.Principal,
1414
1433
  refund_destination: AccountType,
@@ -1416,7 +1435,7 @@ var idlFactory2 = ({ IDL }) => {
1416
1435
  ltv_timer_s: IDL.Nat64,
1417
1436
  lending_profile: IDL.Principal,
1418
1437
  borrow_pool_id: IDL.Principal,
1419
- borrow_asset: Assets
1438
+ borrow_asset: IDL.Unknown
1420
1439
  }),
1421
1440
  FullLendWithdrawalRequested: IDL.Record({
1422
1441
  loan_id: IDL.Nat,
@@ -1467,7 +1486,7 @@ var idlFactory2 = ({ IDL }) => {
1467
1486
  authorisation: AuthorisationType,
1468
1487
  borrow_destination: AccountType,
1469
1488
  started: IDL.Bool,
1470
- lend_asset: Assets,
1489
+ lend_asset: IDL.Unknown,
1471
1490
  created_at: IDL.Nat64,
1472
1491
  schema_version: IDL.Nat16,
1473
1492
  borrow_amount: IDL.Nat,
@@ -1478,7 +1497,7 @@ var idlFactory2 = ({ IDL }) => {
1478
1497
  lending_profile: IDL.Principal,
1479
1498
  expires_at: IDL.Opt(IDL.Nat64),
1480
1499
  borrow_pool_id: IDL.Principal,
1481
- borrow_asset: Assets,
1500
+ borrow_asset: IDL.Unknown,
1482
1501
  deposit_detected_ts: IDL.Opt(IDL.Nat64)
1483
1502
  });
1484
1503
  const LoanResult = IDL.Variant({ Ok: Loan, Err: InstantLoansError });
@@ -1506,7 +1525,7 @@ var idlFactory2 = ({ IDL }) => {
1506
1525
  list_warmed_profiles: IDL.Func([], [IDL.Vec(WarmedProfile)], ["query"])
1507
1526
  });
1508
1527
  };
1509
- function createInstantLoansActor(canisterContext) {
1528
+ function createFlexibleInstantLoansActor(canisterContext) {
1510
1529
  const canisterId = canisterContext.canisterIds.instantLoans;
1511
1530
  if (!canisterId) {
1512
1531
  throw new LiquidiumError(
@@ -1514,10 +1533,84 @@ function createInstantLoansActor(canisterContext) {
1514
1533
  "Instant loans canister ID is not configured"
1515
1534
  );
1516
1535
  }
1517
- return Actor.createActor(idlFactory2, {
1518
- agent: canisterContext.agent,
1519
- canisterId
1520
- });
1536
+ return Actor.createActor(
1537
+ flexibleInstantLoansIdlFactory,
1538
+ {
1539
+ agent: canisterContext.agent,
1540
+ canisterId
1541
+ }
1542
+ );
1543
+ }
1544
+ function decodeFlexibleInstantLoanRecord(record) {
1545
+ const lend_asset = extractVariantTag(record.lend_asset, KNOWN_ASSET_TAGS);
1546
+ const borrow_asset = extractVariantTag(record.borrow_asset, KNOWN_ASSET_TAGS);
1547
+ if (!lend_asset || !borrow_asset) {
1548
+ return null;
1549
+ }
1550
+ return {
1551
+ id: record.id,
1552
+ authorisation: record.authorisation,
1553
+ borrow_destination: record.borrow_destination,
1554
+ started: record.started,
1555
+ lend_asset,
1556
+ created_at: record.created_at,
1557
+ schema_version: record.schema_version,
1558
+ borrow_amount: record.borrow_amount,
1559
+ lend_pool_id: record.lend_pool_id,
1560
+ refund_destination: record.refund_destination,
1561
+ ltv_max_bps: record.ltv_max_bps,
1562
+ ltv_timer_s: record.ltv_timer_s,
1563
+ lending_profile: record.lending_profile,
1564
+ borrow_pool_id: record.borrow_pool_id,
1565
+ borrow_asset,
1566
+ expires_at: record.expires_at,
1567
+ deposit_detected_ts: record.deposit_detected_ts
1568
+ };
1569
+ }
1570
+ function decodeFlexibleHeadlessLoanEvent(event) {
1571
+ if ("LoanCreated" in event.event_type) {
1572
+ const decoded = decodeLoanCreatedEvent(event.event_type.LoanCreated);
1573
+ if (!decoded) {
1574
+ return null;
1575
+ }
1576
+ return {
1577
+ id: event.id,
1578
+ schema_version: event.schema_version,
1579
+ timestamp: event.timestamp,
1580
+ event_type: decoded
1581
+ };
1582
+ }
1583
+ return {
1584
+ id: event.id,
1585
+ schema_version: event.schema_version,
1586
+ timestamp: event.timestamp,
1587
+ event_type: event.event_type
1588
+ };
1589
+ }
1590
+ function decodeLoanCreatedEvent(payload) {
1591
+ const lend_asset = extractVariantTag(payload.lend_asset, KNOWN_ASSET_TAGS);
1592
+ const borrow_asset = extractVariantTag(
1593
+ payload.borrow_asset,
1594
+ KNOWN_ASSET_TAGS
1595
+ );
1596
+ if (!lend_asset || !borrow_asset) {
1597
+ return null;
1598
+ }
1599
+ return {
1600
+ LoanCreated: {
1601
+ loan_id: payload.loan_id,
1602
+ borrow_destination: payload.borrow_destination,
1603
+ lend_asset,
1604
+ borrow_amount: payload.borrow_amount,
1605
+ lend_pool_id: payload.lend_pool_id,
1606
+ refund_destination: payload.refund_destination,
1607
+ ltv_max_bps: payload.ltv_max_bps,
1608
+ ltv_timer_s: payload.ltv_timer_s,
1609
+ lending_profile: payload.lending_profile,
1610
+ borrow_pool_id: payload.borrow_pool_id,
1611
+ borrow_asset
1612
+ }
1613
+ };
1521
1614
  }
1522
1615
 
1523
1616
  // src/core/sdk-api-paths.ts
@@ -1591,9 +1684,9 @@ function buildActivityStatusPath(request) {
1591
1684
  request.id
1592
1685
  )}/status?${query.toString()}`;
1593
1686
  }
1594
- function buildInstantLoanAddressLookupPath(request) {
1595
- const query = new URLSearchParams({ address: request.address });
1596
- return `${INSTANT_LOANS}/address?${query.toString()}`;
1687
+ function buildInstantLoanFindPath(request) {
1688
+ const query = new URLSearchParams({ query: request.query });
1689
+ return `${INSTANT_LOANS}/find?${query.toString()}`;
1597
1690
  }
1598
1691
  function buildInstantLoanCollateralHintPath(request) {
1599
1692
  return `${INSTANT_LOANS}/${encodeURIComponent(
@@ -1807,7 +1900,7 @@ var ActivitiesModule = class {
1807
1900
  );
1808
1901
  }
1809
1902
  try {
1810
- const result = await createInstantLoansActor(
1903
+ const result = await createFlexibleInstantLoansActor(
1811
1904
  this.canisterContext
1812
1905
  ).get_loan(loanId);
1813
1906
  if ("Err" in result) {
@@ -2222,6 +2315,41 @@ function mapHistoryStatusToApi(status) {
2222
2315
  return status.toUpperCase();
2223
2316
  }
2224
2317
 
2318
+ // src/core/utils/api-response-parsers.ts
2319
+ var MILLISECONDS_PER_SECOND2 = 1e3;
2320
+ function parseNonEmptyApiString(value, field) {
2321
+ if (!value) {
2322
+ throwInvalidApiResponseField(field);
2323
+ }
2324
+ return value;
2325
+ }
2326
+ function parseUnsignedApiBigint(value, field) {
2327
+ if (!value || !/^\d+$/.test(value)) {
2328
+ throwInvalidApiResponseField(field);
2329
+ }
2330
+ return BigInt(value);
2331
+ }
2332
+ function parseIsoApiTimestampToUnixSeconds(value, field) {
2333
+ const timestamp = parseNonEmptyApiString(value, field);
2334
+ const timestampMilliseconds = Date.parse(timestamp);
2335
+ if (!Number.isFinite(timestampMilliseconds)) {
2336
+ throwInvalidApiResponseField(field);
2337
+ }
2338
+ return BigInt(Math.floor(timestampMilliseconds / MILLISECONDS_PER_SECOND2));
2339
+ }
2340
+ function parseApiStringUnion(value, allowedValues, field) {
2341
+ if (value !== void 0 && allowedValues.includes(value)) {
2342
+ return value;
2343
+ }
2344
+ throwInvalidApiResponseField(field);
2345
+ }
2346
+ function throwInvalidApiResponseField(field) {
2347
+ throw new LiquidiumError(
2348
+ LiquidiumErrorCode.VALIDATION_ERROR,
2349
+ `Invalid ${field.context} ${field.label}`
2350
+ );
2351
+ }
2352
+
2225
2353
  // src/core/utils/asset-decimals.ts
2226
2354
  var ASSET_NATIVE_DECIMALS = {
2227
2355
  BTC: 8n,
@@ -2241,7 +2369,7 @@ function getAssetNativeDecimals(asset) {
2241
2369
  }
2242
2370
 
2243
2371
  // src/generated/canisters/ck-btc-minter/declaration.js
2244
- var idlFactory3 = ({ IDL }) => {
2372
+ var idlFactory2 = ({ IDL }) => {
2245
2373
  const Mode = IDL.Variant({
2246
2374
  "RestrictedTo": IDL.Vec(IDL.Principal),
2247
2375
  "DepositsRestrictedTo": IDL.Vec(IDL.Principal),
@@ -2621,14 +2749,14 @@ var idlFactory3 = ({ IDL }) => {
2621
2749
  // src/core/canisters/ckbtc/minter.ts
2622
2750
  function createCkBtcMinterActor(canisterContext) {
2623
2751
  const canisterId = CK_CANISTER_IDS.btcMinter;
2624
- return Actor.createActor(idlFactory3, {
2752
+ return Actor.createActor(idlFactory2, {
2625
2753
  agent: canisterContext.agent,
2626
2754
  canisterId
2627
2755
  });
2628
2756
  }
2629
2757
 
2630
2758
  // src/generated/canisters/deposit-accounts/deposit_accounts.did.js
2631
- var idlFactory4 = ({ IDL }) => {
2759
+ var idlFactory3 = ({ IDL }) => {
2632
2760
  const DepositAccountErrors = IDL.Variant({
2633
2761
  "Busy": IDL.Null,
2634
2762
  "NotFound": IDL.Null,
@@ -2848,7 +2976,7 @@ function createDepositAccountsActor(canisterContext) {
2848
2976
  "ETH deposit canister ID is not configured"
2849
2977
  );
2850
2978
  }
2851
- return Actor.createActor(idlFactory4, {
2979
+ return Actor.createActor(idlFactory3, {
2852
2980
  agent: canisterContext.agent,
2853
2981
  canisterId
2854
2982
  });
@@ -3613,6 +3741,14 @@ var RATE_SCALE2 = 10n ** 27n;
3613
3741
  var SECONDS_PER_YEAR = 31536000n;
3614
3742
  var ETH_STABLECOIN_INFLOW_FEE_FALLBACK = 1500000n;
3615
3743
  var INSTANT_LOAN_MIN_SLIPPAGE_BUFFER_BPS = 200n;
3744
+ var INSTANT_LOAN_FIND_QUERY_MAX_LENGTH = 256;
3745
+ var INSTANT_LOAN_WIRE_CONTEXT = "instant loan";
3746
+ var INSTANT_LOAN_ASSETS = [
3747
+ Asset.BTC,
3748
+ Asset.SOL,
3749
+ Asset.USDC,
3750
+ Asset.USDT
3751
+ ];
3616
3752
  var InstantLoansModule = class {
3617
3753
  constructor(canisterContext, apiClient, lending, positions) {
3618
3754
  this.canisterContext = canisterContext;
@@ -3669,7 +3805,19 @@ var InstantLoansModule = class {
3669
3805
  borrowDestination,
3670
3806
  refundDestination
3671
3807
  });
3672
- return await this.mapLoanWire(response.loan);
3808
+ const loanId = parseUnsignedApiBigint(response.loan.loanId, {
3809
+ context: INSTANT_LOAN_WIRE_CONTEXT,
3810
+ label: "loan ID"
3811
+ });
3812
+ const collateralAmount = parseUnsignedApiBigint(
3813
+ response.loan.collateral.amountHint,
3814
+ {
3815
+ context: INSTANT_LOAN_WIRE_CONTEXT,
3816
+ label: "collateral amount"
3817
+ }
3818
+ );
3819
+ const record = await this.getLoanRecord(loanId);
3820
+ return await this.mapLoanRecord(record, collateralAmount);
3673
3821
  }
3674
3822
  /**
3675
3823
  * Resolves canonical canister state by loan id or short reference.
@@ -3682,21 +3830,30 @@ var InstantLoansModule = class {
3682
3830
  */
3683
3831
  async get(request) {
3684
3832
  const loanId = "loanId" in request ? request.loanId : decodeRef(request.ref);
3685
- try {
3686
- const result = await createInstantLoansActor(
3687
- this.canisterContext
3688
- ).get_loan(loanId);
3689
- if ("Err" in result) {
3690
- throw mapInstantLoansErrorToLiquidiumError(result.Err);
3691
- }
3692
- const collateralAmount = await this.getCollateralAmountHint(loanId);
3693
- return await this.mapLoanRecord(result.Ok, collateralAmount);
3694
- } catch (error) {
3695
- if (error instanceof LiquidiumError) {
3696
- throw error;
3697
- }
3698
- throw mapCanisterCallErrorToLiquidiumError("get_loan", error);
3699
- }
3833
+ const record = await this.getLoanRecord(loanId);
3834
+ const collateralAmount = await this.getCollateralAmountHint(loanId);
3835
+ return await this.mapLoanRecord(record, collateralAmount);
3836
+ }
3837
+ /**
3838
+ * Finds instant loans by short reference, numeric loan id string, address, or transaction id.
3839
+ *
3840
+ * Search returns lightweight matches. Call `get({ loanId })` or `get({ ref })`
3841
+ * when the user selects a match and you need hydrated loan state.
3842
+ *
3843
+ * @param query - Short reference, address, transaction id/hash, or numeric loan id string.
3844
+ * @returns Matching loan ids and references from the search index.
3845
+ */
3846
+ async find(query) {
3847
+ const validatedQuery = validateInstantLoanFindQuery(query);
3848
+ const candidates = await this.findCandidateLoansByQuery(validatedQuery);
3849
+ return uniqueInstantLoanFindCandidates(candidates).map((candidate) => ({
3850
+ loanId: candidate.loanId,
3851
+ ref: candidate.ref,
3852
+ createdAt: candidate.createdAt,
3853
+ collateral: candidate.collateral,
3854
+ borrow: candidate.borrow,
3855
+ profileId: candidate.profileId
3856
+ }));
3700
3857
  }
3701
3858
  /**
3702
3859
  * Returns the active instant-loans canister config via direct query.
@@ -3705,7 +3862,7 @@ var InstantLoansModule = class {
3705
3862
  */
3706
3863
  async getConfig() {
3707
3864
  try {
3708
- const config = await createInstantLoansActor(
3865
+ const config = await createFlexibleInstantLoansActor(
3709
3866
  this.canisterContext
3710
3867
  ).get_config();
3711
3868
  return {
@@ -3723,10 +3880,11 @@ var InstantLoansModule = class {
3723
3880
  */
3724
3881
  async getEvent(eventId) {
3725
3882
  try {
3726
- const event = await createInstantLoansActor(
3883
+ const event = await createFlexibleInstantLoansActor(
3727
3884
  this.canisterContext
3728
3885
  ).get_event(eventId);
3729
- return event[0] ? mapInstantLoanEvent(event[0]) : null;
3886
+ const decoded = event[0] ? decodeFlexibleHeadlessLoanEvent(event[0]) : null;
3887
+ return decoded ? mapInstantLoanEvent(decoded) : null;
3730
3888
  } catch (error) {
3731
3889
  throw mapCanisterCallErrorToLiquidiumError("get_event", error);
3732
3890
  }
@@ -3739,10 +3897,10 @@ var InstantLoansModule = class {
3739
3897
  */
3740
3898
  async listEvents(request) {
3741
3899
  try {
3742
- const events = await createInstantLoansActor(
3900
+ const events = await createFlexibleInstantLoansActor(
3743
3901
  this.canisterContext
3744
3902
  ).list_events(request.start, request.limit);
3745
- return events.map(([, event]) => mapInstantLoanEvent(event));
3903
+ return events.map(([, event]) => decodeFlexibleHeadlessLoanEvent(event)).filter((event) => event !== null).map((event) => mapInstantLoanEvent(event));
3746
3904
  } catch (error) {
3747
3905
  throw mapCanisterCallErrorToLiquidiumError("list_events", error);
3748
3906
  }
@@ -3754,7 +3912,7 @@ var InstantLoansModule = class {
3754
3912
  */
3755
3913
  async listAccessList() {
3756
3914
  try {
3757
- const principals = await createInstantLoansActor(
3915
+ const principals = await createFlexibleInstantLoansActor(
3758
3916
  this.canisterContext
3759
3917
  ).list_access_list();
3760
3918
  return principals.map((principal) => principal.toText());
@@ -3769,7 +3927,7 @@ var InstantLoansModule = class {
3769
3927
  */
3770
3928
  async countWarmedProfiles() {
3771
3929
  try {
3772
- return await createInstantLoansActor(
3930
+ return await createFlexibleInstantLoansActor(
3773
3931
  this.canisterContext
3774
3932
  ).count_warmed_profiles();
3775
3933
  } catch (error) {
@@ -3786,7 +3944,7 @@ var InstantLoansModule = class {
3786
3944
  */
3787
3945
  async listWarmedProfiles() {
3788
3946
  try {
3789
- const profiles = await createInstantLoansActor(
3947
+ const profiles = await createFlexibleInstantLoansActor(
3790
3948
  this.canisterContext
3791
3949
  ).list_warmed_profiles();
3792
3950
  return profiles.map(mapWarmedProfile);
@@ -3794,30 +3952,36 @@ var InstantLoansModule = class {
3794
3952
  throw mapCanisterCallErrorToLiquidiumError("list_warmed_profiles", error);
3795
3953
  }
3796
3954
  }
3797
- /**
3798
- * Finds candidate loans associated with an address through the Liquidium SDK
3799
- * API. Returns discovery candidates only; call `get(...)` to hydrate canister state.
3800
- *
3801
- * Candidates are useful for recovery flows where the user knows a borrow or
3802
- * refund address but not the loan reference.
3803
- *
3804
- * @param address - Borrow or refund address to search for.
3805
- * @returns Lightweight loan candidates associated with the address.
3806
- */
3807
- async findByAddress(address) {
3808
- const trimmedAddress = address.trim();
3809
- if (!trimmedAddress) {
3810
- throw new LiquidiumError(
3811
- LiquidiumErrorCode.VALIDATION_ERROR,
3812
- "Address lookup requires a non-empty address"
3813
- );
3814
- }
3815
- const apiClient = this.requireApi("Instant loan address lookup");
3955
+ async findCandidateLoansByQuery(query) {
3956
+ const apiClient = this.requireApi("Instant loan find");
3816
3957
  const response = await apiClient.get(
3817
- buildInstantLoanAddressLookupPath({ address: trimmedAddress })
3958
+ buildInstantLoanFindPath({ query })
3818
3959
  );
3819
3960
  return (response.candidates ?? response.loans ?? []).map(mapCandidateWire);
3820
3961
  }
3962
+ async getLoanRecord(loanId) {
3963
+ try {
3964
+ const result = await createFlexibleInstantLoansActor(
3965
+ this.canisterContext
3966
+ ).get_loan(loanId);
3967
+ if ("Err" in result) {
3968
+ throw mapInstantLoansErrorToLiquidiumError(result.Err);
3969
+ }
3970
+ const decoded = decodeFlexibleInstantLoanRecord(result.Ok);
3971
+ if (!decoded) {
3972
+ throw new LiquidiumError(
3973
+ LiquidiumErrorCode.POOL_NOT_FOUND,
3974
+ `Instant loan ${loanId.toString()} uses an unsupported asset`
3975
+ );
3976
+ }
3977
+ return decoded;
3978
+ } catch (error) {
3979
+ if (error instanceof LiquidiumError) {
3980
+ throw error;
3981
+ }
3982
+ throw mapCanisterCallErrorToLiquidiumError("get_loan", error);
3983
+ }
3984
+ }
3821
3985
  async mapLoanRecord(record, collateralAmount) {
3822
3986
  return await this.hydrateLoan({
3823
3987
  loanId: record.id,
@@ -3827,41 +3991,26 @@ var InstantLoansModule = class {
3827
3991
  collateralPoolId: record.lend_pool_id.toText(),
3828
3992
  collateralAmount,
3829
3993
  borrowPoolId: record.borrow_pool_id.toText(),
3830
- collateralAsset: getVariantKey(record.lend_asset),
3831
- borrowAsset: getVariantKey(record.borrow_asset),
3994
+ collateralAsset: record.lend_asset,
3995
+ borrowAsset: record.borrow_asset,
3832
3996
  borrowAmount: record.borrow_amount,
3833
3997
  borrowDestination: accountFromCanister(record.borrow_destination),
3834
- refundDestination: accountFromCanister(record.refund_destination)
3998
+ refundDestination: accountFromCanister(record.refund_destination),
3999
+ depositDetectedTimestamp: record.deposit_detected_ts[0] ?? null,
4000
+ expiryTimestamp: record.expires_at[0] ?? deriveDepositExpiryTimestamp({
4001
+ depositDetectedTimestamp: record.deposit_detected_ts[0] ?? null,
4002
+ depositWindowSeconds: record.ltv_timer_s
4003
+ })
3835
4004
  });
3836
4005
  }
3837
4006
  async getCollateralAmountHint(loanId) {
3838
- const apiClient = this.requireApi("Instant loan lookup");
4007
+ const apiClient = this.requireApi("Instant loan collateral hint");
3839
4008
  const response = await apiClient.get(
3840
4009
  buildInstantLoanCollateralHintPath({ loanId })
3841
4010
  );
3842
- return parseBigintWire(response.collateralAmountHint, "collateral amount");
3843
- }
3844
- async mapLoanWire(loan) {
3845
- const loanId = parseBigintWire(loan.loanId, "loan ID");
3846
- return await this.hydrateLoan({
3847
- loanId,
3848
- profileId: loan.profileId,
3849
- ltvMaxBps: parseBigintWire(loan.ltvMaxBps, "max LTV"),
3850
- depositWindowSeconds: parseBigintWire(
3851
- loan.depositWindowSeconds,
3852
- "deposit window"
3853
- ),
3854
- collateralPoolId: loan.collateral.poolId,
3855
- collateralAmount: parseBigintWire(
3856
- loan.collateral.amountHint,
3857
- "collateral amount"
3858
- ),
3859
- borrowPoolId: loan.borrow.poolId,
3860
- collateralAsset: loan.collateral.asset,
3861
- borrowAsset: loan.borrow.asset,
3862
- borrowAmount: parseBigintWire(loan.borrow.amount, "borrow amount"),
3863
- borrowDestination: accountFromWire(loan.borrow.destination),
3864
- refundDestination: accountFromWire(loan.refundDestination)
4011
+ return parseUnsignedApiBigint(response.collateralAmountHint, {
4012
+ context: INSTANT_LOAN_WIRE_CONTEXT,
4013
+ label: "collateral amount"
3865
4014
  });
3866
4015
  }
3867
4016
  async hydrateLoan(input) {
@@ -3915,7 +4064,9 @@ var InstantLoansModule = class {
3915
4064
  collateralAmount,
3916
4065
  decimals: collateralDecimals,
3917
4066
  asset: collateralAsset,
3918
- target: depositTarget
4067
+ target: depositTarget,
4068
+ detectedTimestamp: input.depositDetectedTimestamp,
4069
+ expiryTimestamp: input.expiryTimestamp
3919
4070
  });
3920
4071
  const repayment = {
3921
4072
  amount: repaymentAmount,
@@ -3979,7 +4130,9 @@ var InstantLoansModule = class {
3979
4130
  inflowFeeAmount: inflowFee.totalFee,
3980
4131
  asset: input.asset,
3981
4132
  chain: input.target.chain,
3982
- target: input.target
4133
+ target: input.target,
4134
+ detectedTimestamp: input.detectedTimestamp,
4135
+ expiryTimestamp: input.expiryTimestamp
3983
4136
  };
3984
4137
  }
3985
4138
  async estimateRepaymentInflowFee(asset, chain) {
@@ -4075,6 +4228,12 @@ function deriveInstantLoanStatus(input) {
4075
4228
  }
4076
4229
  return InstantLoanStatus.awaitingDeposit;
4077
4230
  }
4231
+ function deriveDepositExpiryTimestamp(input) {
4232
+ if (input.depositDetectedTimestamp === null) {
4233
+ return null;
4234
+ }
4235
+ return input.depositDetectedTimestamp + input.depositWindowSeconds;
4236
+ }
4078
4237
  function validateCreateRequest(request) {
4079
4238
  if (request.collateralAmount <= 0n) {
4080
4239
  throw new LiquidiumError(
@@ -4123,18 +4282,6 @@ function accountFromCanister(account) {
4123
4282
  }
4124
4283
  return { type: "External", address: account.External };
4125
4284
  }
4126
- function accountFromWire(account) {
4127
- if ("Native" in account) {
4128
- return { type: "Native", principal: account.Native };
4129
- }
4130
- if ("External" in account) {
4131
- return { type: "External", address: account.External };
4132
- }
4133
- if (account.type === "Native") {
4134
- return { type: "Native", principal: account.principal };
4135
- }
4136
- return { type: "External", address: account.address };
4137
- }
4138
4285
  function mapInstantLoanEvent(event) {
4139
4286
  return {
4140
4287
  id: event.id,
@@ -4150,7 +4297,7 @@ function mapInstantLoanEventType(eventType) {
4150
4297
  type: "LoanCreated",
4151
4298
  loanId: event.loan_id,
4152
4299
  borrowDestination: accountFromCanister(event.borrow_destination),
4153
- collateralAsset: assetFromCanister(event.lend_asset),
4300
+ collateralAsset: event.lend_asset,
4154
4301
  borrowAmount: event.borrow_amount,
4155
4302
  collateralPoolId: event.lend_pool_id.toText(),
4156
4303
  refundDestination: accountFromCanister(event.refund_destination),
@@ -4158,7 +4305,7 @@ function mapInstantLoanEventType(eventType) {
4158
4305
  depositWindowSeconds: event.ltv_timer_s,
4159
4306
  profileId: event.lending_profile.toText(),
4160
4307
  borrowPoolId: event.borrow_pool_id.toText(),
4161
- borrowAsset: assetFromCanister(event.borrow_asset)
4308
+ borrowAsset: event.borrow_asset
4162
4309
  };
4163
4310
  }
4164
4311
  if ("FullLendWithdrawalRequested" in eventType) {
@@ -4236,9 +4383,6 @@ function authorizationFromCanister(authorization) {
4236
4383
  address: authorization.EthSignature.address
4237
4384
  };
4238
4385
  }
4239
- function assetFromCanister(asset) {
4240
- return getVariantKey(asset);
4241
- }
4242
4386
  function legFromCanister(leg) {
4243
4387
  return getVariantKey(leg);
4244
4388
  }
@@ -4253,55 +4397,80 @@ function decodeRef(ref) {
4253
4397
  );
4254
4398
  }
4255
4399
  }
4256
- function mapCandidateWire(wire) {
4257
- const loanId = parseBigintWire(wire.loanId ?? wire.loan_id, "loan ID");
4258
- const ref = wire.ref ?? wire.short_ref ?? publicIdFromInt(loanId);
4259
- const createdAt = wire.createdAt ?? wire.created_at;
4260
- return {
4261
- loanId,
4262
- ref,
4263
- profileId: requiredString(
4264
- wire.profileId ?? wire.lending_profile,
4265
- "profile ID"
4266
- ),
4267
- ...createdAt ? { createdAt: new Date(createdAt) } : {},
4268
- collateralPoolId: requiredString(
4269
- wire.collateralPoolId ?? wire.lend_pool_ic_id,
4270
- "collateral pool ID"
4271
- ),
4272
- borrowPoolId: requiredString(
4273
- wire.borrowPoolId ?? wire.borrow_pool_ic_id,
4274
- "borrow pool ID"
4275
- ),
4276
- collateralAsset: requiredString(
4277
- wire.collateralAsset ?? wire.lend_asset,
4278
- "collateral asset"
4279
- ),
4280
- borrowAsset: requiredString(
4281
- wire.borrowAsset ?? wire.borrow_asset,
4282
- "borrow asset"
4283
- ),
4284
- collateralAmount: parseBigintWire(
4285
- wire.collateralAmount,
4286
- "collateral amount"
4287
- )
4288
- };
4289
- }
4290
- function parseBigintWire(value, label) {
4291
- if (!value || !/^\d+$/.test(value)) {
4400
+ function validateInstantLoanFindQuery(query) {
4401
+ if (typeof query !== "string") {
4292
4402
  throw new LiquidiumError(
4293
4403
  LiquidiumErrorCode.VALIDATION_ERROR,
4294
- `Invalid instant loan ${label}`
4404
+ "Instant loan find query must be a string"
4295
4405
  );
4296
4406
  }
4297
- return BigInt(value);
4407
+ const trimmedQuery = query.trim();
4408
+ if (!trimmedQuery) {
4409
+ throw new LiquidiumError(
4410
+ LiquidiumErrorCode.VALIDATION_ERROR,
4411
+ "Instant loan find query must be non-empty"
4412
+ );
4413
+ }
4414
+ if (trimmedQuery.length > INSTANT_LOAN_FIND_QUERY_MAX_LENGTH) {
4415
+ throw new LiquidiumError(
4416
+ LiquidiumErrorCode.VALIDATION_ERROR,
4417
+ `Instant loan find query must be at most ${INSTANT_LOAN_FIND_QUERY_MAX_LENGTH.toString()} characters`
4418
+ );
4419
+ }
4420
+ return trimmedQuery;
4298
4421
  }
4299
- function requiredString(value, label) {
4300
- if (value?.trim()) return value;
4301
- throw new LiquidiumError(
4302
- LiquidiumErrorCode.VALIDATION_ERROR,
4303
- `Missing instant loan ${label}`
4304
- );
4422
+ function uniqueInstantLoanFindCandidates(candidates) {
4423
+ const candidatesByLoanId = /* @__PURE__ */ new Map();
4424
+ for (const candidate of candidates) {
4425
+ if (!candidatesByLoanId.has(candidate.loanId)) {
4426
+ candidatesByLoanId.set(candidate.loanId, candidate);
4427
+ }
4428
+ }
4429
+ return [...candidatesByLoanId.values()];
4430
+ }
4431
+ function mapCandidateWire(wire) {
4432
+ return {
4433
+ loanId: parseUnsignedApiBigint(wire.loan_id, {
4434
+ context: INSTANT_LOAN_WIRE_CONTEXT,
4435
+ label: "loan ID"
4436
+ }),
4437
+ ref: parseNonEmptyApiString(wire.short_ref, {
4438
+ context: INSTANT_LOAN_WIRE_CONTEXT,
4439
+ label: "short reference"
4440
+ }),
4441
+ createdAt: parseIsoApiTimestampToUnixSeconds(wire.created_at, {
4442
+ context: INSTANT_LOAN_WIRE_CONTEXT,
4443
+ label: "creation timestamp"
4444
+ }),
4445
+ collateral: {
4446
+ poolId: parseNonEmptyApiString(wire.lend_pool_ic_id, {
4447
+ context: INSTANT_LOAN_WIRE_CONTEXT,
4448
+ label: "lend pool ID"
4449
+ }),
4450
+ asset: parseApiStringUnion(wire.lend_asset, INSTANT_LOAN_ASSETS, {
4451
+ context: INSTANT_LOAN_WIRE_CONTEXT,
4452
+ label: "lend asset"
4453
+ }),
4454
+ amount: parseUnsignedApiBigint(wire.collateral_amount, {
4455
+ context: INSTANT_LOAN_WIRE_CONTEXT,
4456
+ label: "collateral amount"
4457
+ })
4458
+ },
4459
+ borrow: {
4460
+ poolId: parseNonEmptyApiString(wire.borrow_pool_ic_id, {
4461
+ context: INSTANT_LOAN_WIRE_CONTEXT,
4462
+ label: "borrow pool ID"
4463
+ }),
4464
+ asset: parseApiStringUnion(wire.borrow_asset, INSTANT_LOAN_ASSETS, {
4465
+ context: INSTANT_LOAN_WIRE_CONTEXT,
4466
+ label: "borrow asset"
4467
+ })
4468
+ },
4469
+ profileId: parseNonEmptyApiString(wire.profile, {
4470
+ context: INSTANT_LOAN_WIRE_CONTEXT,
4471
+ label: "profile ID"
4472
+ })
4473
+ };
4305
4474
  }
4306
4475
  function mapInstantLoansErrorToLiquidiumError(error) {
4307
4476
  const [key, payload] = Object.entries(error)[0];
@@ -4441,12 +4610,12 @@ function encodeBytes32Hex(bytes) {
4441
4610
  (byte) => byte.toString(16).padStart(2, "0")
4442
4611
  ).join("")}`;
4443
4612
  }
4444
- var idlFactory5 = ({ IDL }) => IDL.Service({
4613
+ var idlFactory4 = ({ IDL }) => IDL.Service({
4445
4614
  icrc1_fee: IDL.Func([], [IDL.Nat], ["query"])
4446
4615
  });
4447
4616
  function createCkBtcLedgerActor(canisterContext) {
4448
4617
  const canisterId = CK_CANISTER_IDS.btcLedger;
4449
- return Actor.createActor(idlFactory5, {
4618
+ return Actor.createActor(idlFactory4, {
4450
4619
  agent: canisterContext.agent,
4451
4620
  canisterId
4452
4621
  });
@@ -5513,21 +5682,203 @@ function shouldSubmitInflow(params) {
5513
5682
  }
5514
5683
  return !isEthStablecoin(params.instruction.asset, params.instruction.chain);
5515
5684
  }
5685
+ var flexibleLendingIdlFactory = ({ IDL }) => {
5686
+ const PoolRecord = IDL.Record({
5687
+ principal: IDL.Principal,
5688
+ asset: IDL.Unknown,
5689
+ chain: IDL.Unknown,
5690
+ total_supply_at_last_sync: IDL.Nat,
5691
+ total_debt_at_last_sync: IDL.Nat,
5692
+ supply_cap: IDL.Opt(IDL.Nat),
5693
+ borrow_cap: IDL.Opt(IDL.Nat),
5694
+ max_ltv: IDL.Nat64,
5695
+ liquidation_threshold: IDL.Nat64,
5696
+ liquidation_bonus: IDL.Nat64,
5697
+ protocol_liquidation_fee: IDL.Nat64,
5698
+ reserve_factor: IDL.Nat64,
5699
+ base_rate: IDL.Nat,
5700
+ optimal_utilization_rate: IDL.Nat,
5701
+ rate_slope_before: IDL.Nat,
5702
+ rate_slope_after: IDL.Nat,
5703
+ lending_index: IDL.Nat,
5704
+ borrow_index: IDL.Nat,
5705
+ same_asset_borrowing: IDL.Opt(IDL.Bool),
5706
+ frozen: IDL.Bool,
5707
+ last_updated: IDL.Opt(IDL.Nat64)
5708
+ });
5709
+ const BorrowingPowerRecord = IDL.Record({
5710
+ max_borrowable_usd: IDL.Nat,
5711
+ weighted_max_ltv: IDL.Nat
5712
+ });
5713
+ const PositionRecord = IDL.Record({
5714
+ asset: IDL.Unknown,
5715
+ total_debt_interest: IDL.Nat,
5716
+ borrow_index_snapshot: IDL.Nat,
5717
+ lending_index_snapshot: IDL.Nat,
5718
+ debt_scaled: IDL.Nat,
5719
+ total_earned_interest: IDL.Nat,
5720
+ deposit_scaled: IDL.Nat,
5721
+ pool_id: IDL.Principal,
5722
+ unpaid_debt_interest: IDL.Nat,
5723
+ last_update: IDL.Nat64,
5724
+ user_profile: IDL.Principal
5725
+ });
5726
+ const UserStatsRecord = IDL.Record({
5727
+ debt: IDL.Nat,
5728
+ collateral: IDL.Nat,
5729
+ acumulated_interest: IDL.Nat,
5730
+ borrowing_power: BorrowingPowerRecord,
5731
+ positions: IDL.Vec(PositionRecord),
5732
+ weighted_liquidation_threshold: IDL.Nat
5733
+ });
5734
+ const PositionViewRecord = IDL.Record({
5735
+ lending_index_now: IDL.Nat,
5736
+ interest_since_snapshot: IDL.Nat,
5737
+ asset: IDL.Unknown,
5738
+ total_debt_interest: IDL.Nat,
5739
+ borrow_index_snapshot: IDL.Nat,
5740
+ debt_native_now: IDL.Nat,
5741
+ borrow_index_now: IDL.Nat,
5742
+ lending_index_snapshot: IDL.Nat,
5743
+ debt_scaled: IDL.Nat,
5744
+ total_earned_interest: IDL.Nat,
5745
+ deposit_scaled: IDL.Nat,
5746
+ earned_since_snapshot: IDL.Nat,
5747
+ deposited_native_now: IDL.Nat,
5748
+ pool_id: IDL.Principal,
5749
+ last_update: IDL.Nat64,
5750
+ user_profile: IDL.Principal
5751
+ });
5752
+ return IDL.Service({
5753
+ list_pools: IDL.Func([], [IDL.Vec(PoolRecord)], ["query"]),
5754
+ get_pool_rate: IDL.Func(
5755
+ [IDL.Principal],
5756
+ [IDL.Opt(IDL.Tuple(IDL.Nat, IDL.Nat, IDL.Nat))],
5757
+ ["query"]
5758
+ ),
5759
+ get_health_factor: IDL.Func(
5760
+ [IDL.Principal],
5761
+ [IDL.Nat, UserStatsRecord],
5762
+ ["query"]
5763
+ ),
5764
+ get_profile_stats: IDL.Func([IDL.Principal], [UserStatsRecord], ["query"]),
5765
+ get_position: IDL.Func(
5766
+ [IDL.Principal, IDL.Principal],
5767
+ [IDL.Opt(PositionViewRecord)],
5768
+ ["query"]
5769
+ )
5770
+ });
5771
+ };
5772
+ function createFlexibleLendingActor(canisterContext) {
5773
+ const canisterId = canisterContext.canisterIds.lending;
5774
+ if (!canisterId) {
5775
+ throw new LiquidiumError(
5776
+ LiquidiumErrorCode.SERVICE_UNAVAILABLE,
5777
+ "Lending canister ID is not configured"
5778
+ );
5779
+ }
5780
+ return Actor.createActor(flexibleLendingIdlFactory, {
5781
+ agent: canisterContext.agent,
5782
+ canisterId
5783
+ });
5784
+ }
5785
+ function decodeFlexiblePool(pool) {
5786
+ const asset = extractVariantTag(pool.asset, KNOWN_ASSET_TAGS);
5787
+ const chain = extractVariantTag(pool.chain, KNOWN_CHAIN_TAGS);
5788
+ if (!asset || !chain) {
5789
+ return null;
5790
+ }
5791
+ return {
5792
+ principal: pool.principal,
5793
+ asset,
5794
+ chain,
5795
+ total_supply_at_last_sync: pool.total_supply_at_last_sync,
5796
+ total_debt_at_last_sync: pool.total_debt_at_last_sync,
5797
+ supply_cap: pool.supply_cap,
5798
+ borrow_cap: pool.borrow_cap,
5799
+ max_ltv: pool.max_ltv,
5800
+ liquidation_threshold: pool.liquidation_threshold,
5801
+ liquidation_bonus: pool.liquidation_bonus,
5802
+ protocol_liquidation_fee: pool.protocol_liquidation_fee,
5803
+ reserve_factor: pool.reserve_factor,
5804
+ base_rate: pool.base_rate,
5805
+ optimal_utilization_rate: pool.optimal_utilization_rate,
5806
+ rate_slope_before: pool.rate_slope_before,
5807
+ rate_slope_after: pool.rate_slope_after,
5808
+ lending_index: pool.lending_index,
5809
+ borrow_index: pool.borrow_index,
5810
+ same_asset_borrowing: pool.same_asset_borrowing,
5811
+ frozen: pool.frozen,
5812
+ last_updated: pool.last_updated
5813
+ };
5814
+ }
5815
+ function decodeFlexiblePosition(position) {
5816
+ const asset = extractVariantTag(position.asset, KNOWN_ASSET_TAGS);
5817
+ if (!asset) {
5818
+ return null;
5819
+ }
5820
+ return {
5821
+ asset,
5822
+ total_debt_interest: position.total_debt_interest,
5823
+ borrow_index_snapshot: position.borrow_index_snapshot,
5824
+ lending_index_snapshot: position.lending_index_snapshot,
5825
+ debt_scaled: position.debt_scaled,
5826
+ total_earned_interest: position.total_earned_interest,
5827
+ deposit_scaled: position.deposit_scaled,
5828
+ pool_id: position.pool_id,
5829
+ unpaid_debt_interest: position.unpaid_debt_interest,
5830
+ last_update: position.last_update,
5831
+ user_profile: position.user_profile
5832
+ };
5833
+ }
5834
+ function decodeFlexiblePositionView(view) {
5835
+ const asset = extractVariantTag(view.asset, KNOWN_ASSET_TAGS);
5836
+ if (!asset) {
5837
+ return null;
5838
+ }
5839
+ return {
5840
+ lending_index_now: view.lending_index_now,
5841
+ interest_since_snapshot: view.interest_since_snapshot,
5842
+ asset,
5843
+ total_debt_interest: view.total_debt_interest,
5844
+ borrow_index_snapshot: view.borrow_index_snapshot,
5845
+ debt_native_now: view.debt_native_now,
5846
+ borrow_index_now: view.borrow_index_now,
5847
+ lending_index_snapshot: view.lending_index_snapshot,
5848
+ debt_scaled: view.debt_scaled,
5849
+ total_earned_interest: view.total_earned_interest,
5850
+ deposit_scaled: view.deposit_scaled,
5851
+ earned_since_snapshot: view.earned_since_snapshot,
5852
+ deposited_native_now: view.deposited_native_now,
5853
+ pool_id: view.pool_id,
5854
+ last_update: view.last_update,
5855
+ user_profile: view.user_profile
5856
+ };
5857
+ }
5858
+ function decodeFlexibleUserStats(stats) {
5859
+ return {
5860
+ debt: stats.debt,
5861
+ collateral: stats.collateral,
5862
+ acumulated_interest: stats.acumulated_interest,
5863
+ borrowing_power: stats.borrowing_power,
5864
+ positions: stats.positions.map(decodeFlexiblePosition).filter((position) => position !== null),
5865
+ weighted_liquidation_threshold: stats.weighted_liquidation_threshold
5866
+ };
5867
+ }
5516
5868
 
5517
5869
  // src/modules/market/mappers.ts
5518
5870
  var DECIMAL_BASE = 10;
5519
5871
  var PAIR_SEPARATOR = "_";
5520
5872
  var USDT_SYMBOL = "USDT";
5521
- function mapLendingPoolRecordToPool(pool, rate) {
5522
- const asset = getVariantKey(pool.asset);
5873
+ function mapDecodedPoolToPool(pool, rate) {
5523
5874
  const totalSupply = pool.total_supply_at_last_sync;
5524
5875
  const totalDebt = pool.total_debt_at_last_sync;
5525
5876
  const availableLiquidity = totalSupply > totalDebt ? totalSupply - totalDebt : 0n;
5526
5877
  return {
5527
5878
  id: pool.principal.toString(),
5528
- asset,
5529
- chain: getVariantKey(pool.chain),
5530
- decimals: getAssetNativeDecimals(asset),
5879
+ asset: pool.asset,
5880
+ chain: pool.chain,
5881
+ decimals: getAssetNativeDecimals(pool.asset),
5531
5882
  frozen: pool.frozen,
5532
5883
  totalSupply,
5533
5884
  totalDebt,
@@ -5600,13 +5951,14 @@ var MarketModule = class {
5600
5951
  async listPools() {
5601
5952
  void this.apiClient;
5602
5953
  try {
5603
- const lendingActor = createLendingActor(this.canisterContext);
5604
- const lendingPools = await lendingActor.list_pools();
5954
+ const flexibleActor = createFlexibleLendingActor(this.canisterContext);
5955
+ const rawPools = await flexibleActor.list_pools();
5956
+ const decodedPools = rawPools.map(decodeFlexiblePool).filter((pool) => pool !== null);
5605
5957
  return await Promise.all(
5606
- lendingPools.map(async (pool) => {
5607
- const poolRate = await lendingActor.get_pool_rate(pool.principal);
5958
+ decodedPools.map(async (pool) => {
5959
+ const poolRate = await flexibleActor.get_pool_rate(pool.principal);
5608
5960
  const resolvedPoolRate = poolRate[0] ?? ZERO_POOL_RATE;
5609
- return mapLendingPoolRecordToPool(pool, resolvedPoolRate);
5961
+ return mapDecodedPoolToPool(pool, resolvedPoolRate);
5610
5962
  })
5611
5963
  );
5612
5964
  } catch (error) {
@@ -5711,12 +6063,11 @@ var MarketModule = class {
5711
6063
 
5712
6064
  // src/modules/positions/mappers.ts
5713
6065
  var USD_VALUE_SCALE_DECIMALS = 27n;
5714
- function mapPositionViewToPosition(view) {
5715
- const asset = getVariantKey(view.asset);
5716
- const nativeDecimals = getAssetNativeDecimals(asset);
6066
+ function mapDecodedPositionViewToPosition(view) {
6067
+ const nativeDecimals = getAssetNativeDecimals(view.asset);
5717
6068
  return {
5718
6069
  poolId: view.pool_id.toText(),
5719
- asset,
6070
+ asset: view.asset,
5720
6071
  deposited: view.deposited_native_now,
5721
6072
  depositedDecimals: nativeDecimals,
5722
6073
  borrowed: view.debt_native_now,
@@ -5733,15 +6084,18 @@ function mapBorrowingPowerRecordToBorrowingPower(record) {
5733
6084
  maxBorrowableUsdDecimals: USD_VALUE_SCALE_DECIMALS
5734
6085
  };
5735
6086
  }
5736
- function mapUserStatsRecordToUserStats(record) {
6087
+ function mapDecodedUserStatsToUserStats(stats) {
6088
+ return mapUserStatsLikeToUserStats(stats);
6089
+ }
6090
+ function mapUserStatsLikeToUserStats(stats) {
5737
6091
  return {
5738
- debt: record.debt,
6092
+ debt: stats.debt,
5739
6093
  debtDecimals: USD_VALUE_SCALE_DECIMALS,
5740
- collateral: record.collateral,
6094
+ collateral: stats.collateral,
5741
6095
  collateralDecimals: USD_VALUE_SCALE_DECIMALS,
5742
- weightedLiquidationThreshold: record.weighted_liquidation_threshold,
6096
+ weightedLiquidationThreshold: stats.weighted_liquidation_threshold,
5743
6097
  borrowingPower: mapBorrowingPowerRecordToBorrowingPower(
5744
- record.borrowing_power
6098
+ stats.borrowing_power
5745
6099
  )
5746
6100
  };
5747
6101
  }
@@ -5765,14 +6119,18 @@ var PositionsModule = class {
5765
6119
  */
5766
6120
  async getPosition(profileId, poolId) {
5767
6121
  try {
5768
- const result = await createLendingActor(
6122
+ const result = await createFlexibleLendingActor(
5769
6123
  this.canisterContext
5770
6124
  ).get_position(Principal.fromText(profileId), Principal.fromText(poolId));
5771
6125
  const view = result[0];
5772
6126
  if (!view) {
5773
6127
  return null;
5774
6128
  }
5775
- return mapPositionViewToPosition(view);
6129
+ const decodedView = decodeFlexiblePositionView(view);
6130
+ if (!decodedView) {
6131
+ return null;
6132
+ }
6133
+ return mapDecodedPositionViewToPosition(decodedView);
5776
6134
  } catch (error) {
5777
6135
  if (error instanceof LiquidiumError) {
5778
6136
  throw error;
@@ -5788,15 +6146,16 @@ var PositionsModule = class {
5788
6146
  */
5789
6147
  async listPositions(profileId) {
5790
6148
  try {
5791
- const actor = createLendingActor(this.canisterContext);
6149
+ const actor = createFlexibleLendingActor(this.canisterContext);
5792
6150
  const profilePrincipal = Principal.fromText(profileId);
5793
6151
  const stats = await actor.get_profile_stats(profilePrincipal);
6152
+ const decodedStats = decodeFlexibleUserStats(stats);
5794
6153
  const positionViews = await Promise.all(
5795
- stats.positions.map(
6154
+ decodedStats.positions.map(
5796
6155
  (position) => actor.get_position(profilePrincipal, position.pool_id)
5797
6156
  )
5798
6157
  );
5799
- return positionViews.map((result) => result[0]).filter((view) => view !== void 0).map(mapPositionViewToPosition);
6158
+ return positionViews.map((result) => result[0]).filter((view) => view !== void 0).map(decodeFlexiblePositionView).filter((view) => view !== null).map(mapDecodedPositionViewToPosition);
5800
6159
  } catch (error) {
5801
6160
  if (error instanceof LiquidiumError) {
5802
6161
  throw error;
@@ -5812,12 +6171,14 @@ var PositionsModule = class {
5812
6171
  */
5813
6172
  async getHealthFactor(profileId) {
5814
6173
  try {
5815
- const [healthFactor, userStatsRecord] = await createLendingActor(
6174
+ const [healthFactor, userStatsRecord] = await createFlexibleLendingActor(
5816
6175
  this.canisterContext
5817
6176
  ).get_health_factor(Principal.fromText(profileId));
5818
6177
  return {
5819
6178
  healthFactor,
5820
- userStats: mapUserStatsRecordToUserStats(userStatsRecord)
6179
+ userStats: mapDecodedUserStatsToUserStats(
6180
+ decodeFlexibleUserStats(userStatsRecord)
6181
+ )
5821
6182
  };
5822
6183
  } catch (error) {
5823
6184
  if (error instanceof LiquidiumError) {
@@ -5834,10 +6195,10 @@ var PositionsModule = class {
5834
6195
  */
5835
6196
  async getUserStats(profileId) {
5836
6197
  try {
5837
- const result = await createLendingActor(
6198
+ const result = await createFlexibleLendingActor(
5838
6199
  this.canisterContext
5839
6200
  ).get_profile_stats(Principal.fromText(profileId));
5840
- return mapUserStatsRecordToUserStats(result);
6201
+ return mapDecodedUserStatsToUserStats(decodeFlexibleUserStats(result));
5841
6202
  } catch (error) {
5842
6203
  if (error instanceof LiquidiumError) {
5843
6204
  throw error;
@@ -5906,7 +6267,7 @@ var PositionsModule = class {
5906
6267
  pool,
5907
6268
  priceUsd,
5908
6269
  suppliedUsd: nativeAmountToUsdScaled(
5909
- position.deposited + position.earnedInterest,
6270
+ position.deposited,
5910
6271
  position.depositedDecimals,
5911
6272
  priceUsd
5912
6273
  ),
@@ -5941,6 +6302,24 @@ var PositionsModule = class {
5941
6302
  const buffered = rawDebt * (BPS_SCALE + bufferBps) / BPS_SCALE;
5942
6303
  return { amount: buffered, decimals: position.borrowedDecimals };
5943
6304
  }
6305
+ /**
6306
+ * Returns the current full withdraw amount for a position.
6307
+ *
6308
+ * `Position.deposited` already reflects the current supplied balance at the
6309
+ * latest lending index; do not add `earnedInterest` to this amount.
6310
+ * Pass `amount` to withdraw calls and use `decimals` for display formatting.
6311
+ *
6312
+ * @param profileId - The Liquidium profile principal text.
6313
+ * @param poolId - The pool principal text.
6314
+ * @returns Full withdraw amount in the supplied asset's base units.
6315
+ */
6316
+ async getFullWithdrawAmount(profileId, poolId) {
6317
+ const position = await this.getPosition(profileId, poolId);
6318
+ if (!position) {
6319
+ return { amount: 0n, decimals: 0n };
6320
+ }
6321
+ return { amount: position.deposited, decimals: position.depositedDecimals };
6322
+ }
5944
6323
  };
5945
6324
  function nativeAmountToUsdScaled(amount, nativeDecimals, priceUsd) {
5946
6325
  if (amount <= 0n || priceUsd <= 0) {