@liquidium/client 0.2.1 → 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/README.md +42 -7
- package/dist/index.cjs +212 -123
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +77 -46
- package/dist/index.d.ts +77 -46
- package/dist/index.js +212 -123
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -38,8 +38,8 @@ Liquidium supports two borrowing and lending integration paths:
|
|
|
38
38
|
|
|
39
39
|
| Path | Use when | Main SDK calls |
|
|
40
40
|
| --- | --- | --- |
|
|
41
|
-
| Recommended: accountless instant loans | You want a short checkout-style flow where users borrow against collateral without creating a Liquidium profile first | `client.instantLoans.create(...)`, `client.instantLoans.get(...)`, `client.activities.list(...)` |
|
|
42
|
-
| Account-based profile flows | You want users to keep a Liquidium profile, manage positions across sessions, supply funds, borrow, withdraw, or use ETH contract-interaction deposits | `client.accounts.createProfile(...)`, `client.lending.supply(...)`, `client.lending.borrow(...)`, `client.lending.withdraw(...)`, `client.positions.
|
|
41
|
+
| Recommended: accountless instant loans | You want a short checkout-style flow where users borrow against collateral without creating a Liquidium profile first | `client.instantLoans.create(...)`, `client.instantLoans.get(...)`, `client.instantLoans.find(...)`, `client.activities.list(...)` |
|
|
42
|
+
| Account-based profile flows | You want users to keep a Liquidium profile, manage positions across sessions, supply funds, borrow, withdraw, or use ETH contract-interaction deposits | `client.accounts.createProfile(...)`, `client.lending.supply(...)`, `client.lending.borrow(...)`, `client.lending.withdraw(...)`, `client.positions.listPositions(...)` |
|
|
43
43
|
|
|
44
44
|
Use accountless instant loans for new borrow flows unless you need profile-level position management. Use account-based flows when your app owns the full lending dashboard experience.
|
|
45
45
|
|
|
@@ -118,6 +118,11 @@ const activities = await client.activities.list({ shortRef: loan.ref });
|
|
|
118
118
|
|
|
119
119
|
console.log("Loan activities:", activities);
|
|
120
120
|
|
|
121
|
+
const foundLoans = await client.instantLoans.find(loan.ref);
|
|
122
|
+
|
|
123
|
+
console.log("Found loan reference:", foundLoans[0]?.ref);
|
|
124
|
+
console.log("Found loan collateral:", foundLoans[0]?.collateral.amount.toString());
|
|
125
|
+
|
|
121
126
|
function requirePool(pools: Pool[], asset: string): Pool {
|
|
122
127
|
const pool = pools.find((candidatePool) => candidatePool.asset === asset);
|
|
123
128
|
|
|
@@ -150,7 +155,7 @@ Instant-loan integrations use this sequence:
|
|
|
150
155
|
| Load market data | `client.market.listPools()` and `client.market.getAssetPrices()` | Show supported collateral and borrow assets |
|
|
151
156
|
| Validate amounts | `client.quote.calculateLtv(...)` | Block too-small borrow amounts, invalid LTV, or frozen-pool input before creating a loan |
|
|
152
157
|
| Create loan | `client.instantLoans.create(...)` | Store `loan.ref` and show `loan.initialDeposit.amount` plus `loan.initialDeposit.target` |
|
|
153
|
-
| Track loan | `client.instantLoans.get({ ref })` and `client.activities.list({ shortRef: ref })` | Reload loan state, initial deposit quote, and repayment activity |
|
|
158
|
+
| Track loan | `client.instantLoans.get({ ref })`, `client.instantLoans.find(...)`, and `client.activities.list({ shortRef: ref })` | Reload loan state, initial deposit quote, and repayment activity |
|
|
154
159
|
| 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 |
|
|
155
160
|
|
|
156
161
|
`client.instantLoans.create(...)` and `client.instantLoans.get(...)` return the generated Liquidium profile, current position state, an initial deposit quote with its transfer target, and a repayment quote with its transfer target. Repayment amount fields are zero when the loan has no debt. Users do not manage the generated profile.
|
|
@@ -188,11 +193,33 @@ Loads loan state by numeric canister loan id.
|
|
|
188
193
|
|
|
189
194
|
Use this when your backend stores the numeric id instead of the short reference.
|
|
190
195
|
|
|
191
|
-
### `client.instantLoans.
|
|
196
|
+
### `client.instantLoans.find(query)`
|
|
197
|
+
|
|
198
|
+
Finds instant loans by short reference, numeric canister loan id string, generated deposit or repayment address, borrow or refund destination address, or indexed transaction id/hash through the SDK API search index.
|
|
199
|
+
|
|
200
|
+
Use this for recovery and manage pages where the user may paste any loan identifier. The method returns lightweight matches because address and transaction-id lookups can match many loans. Use `client.instantLoans.get(...)` after the user selects a match.
|
|
201
|
+
|
|
202
|
+
```ts
|
|
203
|
+
const results = await client.instantLoans.find("bc1q...");
|
|
204
|
+
const byRef = await client.instantLoans.find("ABC123");
|
|
205
|
+
const byLoanId = await client.instantLoans.find("42");
|
|
206
|
+
|
|
207
|
+
for (const result of results) {
|
|
208
|
+
console.log(result.ref);
|
|
209
|
+
console.log(result.loanId);
|
|
210
|
+
console.log(result.collateral.asset, result.borrow.asset);
|
|
211
|
+
console.log(result.collateral.amount);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const selectedLoan = await client.instantLoans.get({ loanId: results[0].loanId });
|
|
215
|
+
```
|
|
192
216
|
|
|
193
|
-
|
|
217
|
+
Call `get(...)` when you already have an exact canister identifier and want direct canister lookup without an array:
|
|
194
218
|
|
|
195
|
-
|
|
219
|
+
```ts
|
|
220
|
+
const loanById = await client.instantLoans.get({ loanId: 123n });
|
|
221
|
+
const loanByRef = await client.instantLoans.get({ ref: "ABC123" });
|
|
222
|
+
```
|
|
196
223
|
|
|
197
224
|
### `client.quote.calculateLtv(request, pools, prices)`
|
|
198
225
|
|
|
@@ -212,6 +239,10 @@ The SDK enforces product minimums before borrow creation:
|
|
|
212
239
|
|
|
213
240
|
Use `getMinimumBorrowAmount(asset)` to display the same minimum that `client.quote.calculateLtv(...)`, `client.quote.getQuote(...)`, `client.instantLoans.create(...)`, and `client.lending.prepareBorrow(...)` enforce.
|
|
214
241
|
|
|
242
|
+
### Profile full withdraw amounts
|
|
243
|
+
|
|
244
|
+
For profile-based withdraw flows, call `client.positions.getFullWithdrawAmount(profileId, poolId)` before building the withdraw request. The helper returns `{ amount, decimals }`: pass `amount` to `client.lending.withdraw(...)` or `client.lending.prepareWithdraw(...)`, and use `decimals` for display formatting. Do not add `earnedInterest`; the returned amount already uses the current supplied balance.
|
|
245
|
+
|
|
215
246
|
## Response Fields
|
|
216
247
|
|
|
217
248
|
Most instant-loan UIs show or store these fields:
|
|
@@ -223,10 +254,14 @@ Most instant-loan UIs show or store these fields:
|
|
|
223
254
|
| `loan.initialDeposit.amount` | Fee-inclusive collateral amount to send after creation or restore |
|
|
224
255
|
| `loan.initialDeposit.collateralAmount` | Intended credited collateral target used for LTV |
|
|
225
256
|
| `loan.initialDeposit.target` | Address or ICRC account where the user sends collateral |
|
|
257
|
+
| `loan.initialDeposit.detectedTimestamp` | Unix timestamp in seconds when the collateral deposit was detected, or `null` before detection |
|
|
258
|
+
| `loan.initialDeposit.expiryTimestamp` | Unix timestamp in seconds when the collateral deposit window expires, or `null` before detection when unavailable |
|
|
226
259
|
| `loan.repayment.amount` | Full amount to repay, including fee and interest buffer. Zero when no repayment is due |
|
|
227
260
|
| `loan.repayment.target` | Address or ICRC account where the user sends repayment |
|
|
228
261
|
| `loan.position` | Current collateral, debt, and interest state for the generated profile |
|
|
229
262
|
|
|
263
|
+
`client.instantLoans.find(...)` returns lightweight search matches with `loanId`, `ref`, `createdAt`, `profileId`, `collateral`, and `borrow`. Use `client.instantLoans.get(...)` to load full loan fields, and use `client.activities.list(...)` separately when you need deposit, borrow, repay, or withdraw activity.
|
|
264
|
+
|
|
230
265
|
## Amounts
|
|
231
266
|
|
|
232
267
|
The SDK returns amount fields as `bigint` values in the asset's smallest unit.
|
|
@@ -266,7 +301,7 @@ Start browser integrations with the examples.
|
|
|
266
301
|
|
|
267
302
|
| Example | What it shows |
|
|
268
303
|
| --- | --- |
|
|
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
|
|
304
|
+
| [`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 loan recovery |
|
|
270
305
|
| [`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 |
|
|
271
306
|
|
|
272
307
|
Run the instant loan example:
|
package/dist/index.cjs
CHANGED
|
@@ -1593,9 +1593,9 @@ function buildActivityStatusPath(request) {
|
|
|
1593
1593
|
request.id
|
|
1594
1594
|
)}/status?${query.toString()}`;
|
|
1595
1595
|
}
|
|
1596
|
-
function
|
|
1597
|
-
const query = new URLSearchParams({
|
|
1598
|
-
return `${INSTANT_LOANS}/
|
|
1596
|
+
function buildInstantLoanFindPath(request) {
|
|
1597
|
+
const query = new URLSearchParams({ query: request.query });
|
|
1598
|
+
return `${INSTANT_LOANS}/find?${query.toString()}`;
|
|
1599
1599
|
}
|
|
1600
1600
|
function buildInstantLoanCollateralHintPath(request) {
|
|
1601
1601
|
return `${INSTANT_LOANS}/${encodeURIComponent(
|
|
@@ -2224,6 +2224,41 @@ function mapHistoryStatusToApi(status) {
|
|
|
2224
2224
|
return status.toUpperCase();
|
|
2225
2225
|
}
|
|
2226
2226
|
|
|
2227
|
+
// src/core/utils/api-response-parsers.ts
|
|
2228
|
+
var MILLISECONDS_PER_SECOND2 = 1e3;
|
|
2229
|
+
function parseNonEmptyApiString(value, field) {
|
|
2230
|
+
if (!value) {
|
|
2231
|
+
throwInvalidApiResponseField(field);
|
|
2232
|
+
}
|
|
2233
|
+
return value;
|
|
2234
|
+
}
|
|
2235
|
+
function parseUnsignedApiBigint(value, field) {
|
|
2236
|
+
if (!value || !/^\d+$/.test(value)) {
|
|
2237
|
+
throwInvalidApiResponseField(field);
|
|
2238
|
+
}
|
|
2239
|
+
return BigInt(value);
|
|
2240
|
+
}
|
|
2241
|
+
function parseIsoApiTimestampToUnixSeconds(value, field) {
|
|
2242
|
+
const timestamp = parseNonEmptyApiString(value, field);
|
|
2243
|
+
const timestampMilliseconds = Date.parse(timestamp);
|
|
2244
|
+
if (!Number.isFinite(timestampMilliseconds)) {
|
|
2245
|
+
throwInvalidApiResponseField(field);
|
|
2246
|
+
}
|
|
2247
|
+
return BigInt(Math.floor(timestampMilliseconds / MILLISECONDS_PER_SECOND2));
|
|
2248
|
+
}
|
|
2249
|
+
function parseApiStringUnion(value, allowedValues, field) {
|
|
2250
|
+
if (value !== void 0 && allowedValues.includes(value)) {
|
|
2251
|
+
return value;
|
|
2252
|
+
}
|
|
2253
|
+
throwInvalidApiResponseField(field);
|
|
2254
|
+
}
|
|
2255
|
+
function throwInvalidApiResponseField(field) {
|
|
2256
|
+
throw new LiquidiumError(
|
|
2257
|
+
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
2258
|
+
`Invalid ${field.context} ${field.label}`
|
|
2259
|
+
);
|
|
2260
|
+
}
|
|
2261
|
+
|
|
2227
2262
|
// src/core/utils/asset-decimals.ts
|
|
2228
2263
|
var ASSET_NATIVE_DECIMALS = {
|
|
2229
2264
|
BTC: 8n,
|
|
@@ -3615,6 +3650,14 @@ var RATE_SCALE2 = 10n ** 27n;
|
|
|
3615
3650
|
var SECONDS_PER_YEAR = 31536000n;
|
|
3616
3651
|
var ETH_STABLECOIN_INFLOW_FEE_FALLBACK = 1500000n;
|
|
3617
3652
|
var INSTANT_LOAN_MIN_SLIPPAGE_BUFFER_BPS = 200n;
|
|
3653
|
+
var INSTANT_LOAN_FIND_QUERY_MAX_LENGTH = 256;
|
|
3654
|
+
var INSTANT_LOAN_WIRE_CONTEXT = "instant loan";
|
|
3655
|
+
var INSTANT_LOAN_ASSETS = [
|
|
3656
|
+
Asset.BTC,
|
|
3657
|
+
Asset.SOL,
|
|
3658
|
+
Asset.USDC,
|
|
3659
|
+
Asset.USDT
|
|
3660
|
+
];
|
|
3618
3661
|
var InstantLoansModule = class {
|
|
3619
3662
|
constructor(canisterContext, apiClient, lending, positions) {
|
|
3620
3663
|
this.canisterContext = canisterContext;
|
|
@@ -3671,7 +3714,19 @@ var InstantLoansModule = class {
|
|
|
3671
3714
|
borrowDestination,
|
|
3672
3715
|
refundDestination
|
|
3673
3716
|
});
|
|
3674
|
-
|
|
3717
|
+
const loanId = parseUnsignedApiBigint(response.loan.loanId, {
|
|
3718
|
+
context: INSTANT_LOAN_WIRE_CONTEXT,
|
|
3719
|
+
label: "loan ID"
|
|
3720
|
+
});
|
|
3721
|
+
const collateralAmount = parseUnsignedApiBigint(
|
|
3722
|
+
response.loan.collateral.amountHint,
|
|
3723
|
+
{
|
|
3724
|
+
context: INSTANT_LOAN_WIRE_CONTEXT,
|
|
3725
|
+
label: "collateral amount"
|
|
3726
|
+
}
|
|
3727
|
+
);
|
|
3728
|
+
const record = await this.getLoanRecord(loanId);
|
|
3729
|
+
return await this.mapLoanRecord(record, collateralAmount);
|
|
3675
3730
|
}
|
|
3676
3731
|
/**
|
|
3677
3732
|
* Resolves canonical canister state by loan id or short reference.
|
|
@@ -3684,21 +3739,30 @@ var InstantLoansModule = class {
|
|
|
3684
3739
|
*/
|
|
3685
3740
|
async get(request) {
|
|
3686
3741
|
const loanId = "loanId" in request ? request.loanId : decodeRef(request.ref);
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3742
|
+
const record = await this.getLoanRecord(loanId);
|
|
3743
|
+
const collateralAmount = await this.getCollateralAmountHint(loanId);
|
|
3744
|
+
return await this.mapLoanRecord(record, collateralAmount);
|
|
3745
|
+
}
|
|
3746
|
+
/**
|
|
3747
|
+
* Finds instant loans by short reference, numeric loan id string, address, or transaction id.
|
|
3748
|
+
*
|
|
3749
|
+
* Search returns lightweight matches. Call `get({ loanId })` or `get({ ref })`
|
|
3750
|
+
* when the user selects a match and you need hydrated loan state.
|
|
3751
|
+
*
|
|
3752
|
+
* @param query - Short reference, address, transaction id/hash, or numeric loan id string.
|
|
3753
|
+
* @returns Matching loan ids and references from the search index.
|
|
3754
|
+
*/
|
|
3755
|
+
async find(query) {
|
|
3756
|
+
const validatedQuery = validateInstantLoanFindQuery(query);
|
|
3757
|
+
const candidates = await this.findCandidateLoansByQuery(validatedQuery);
|
|
3758
|
+
return uniqueInstantLoanFindCandidates(candidates).map((candidate) => ({
|
|
3759
|
+
loanId: candidate.loanId,
|
|
3760
|
+
ref: candidate.ref,
|
|
3761
|
+
createdAt: candidate.createdAt,
|
|
3762
|
+
collateral: candidate.collateral,
|
|
3763
|
+
borrow: candidate.borrow,
|
|
3764
|
+
profileId: candidate.profileId
|
|
3765
|
+
}));
|
|
3702
3766
|
}
|
|
3703
3767
|
/**
|
|
3704
3768
|
* Returns the active instant-loans canister config via direct query.
|
|
@@ -3796,30 +3860,29 @@ var InstantLoansModule = class {
|
|
|
3796
3860
|
throw mapCanisterCallErrorToLiquidiumError("list_warmed_profiles", error);
|
|
3797
3861
|
}
|
|
3798
3862
|
}
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
* API. Returns discovery candidates only; call `get(...)` to hydrate canister state.
|
|
3802
|
-
*
|
|
3803
|
-
* Candidates are useful for recovery flows where the user knows a borrow or
|
|
3804
|
-
* refund address but not the loan reference.
|
|
3805
|
-
*
|
|
3806
|
-
* @param address - Borrow or refund address to search for.
|
|
3807
|
-
* @returns Lightweight loan candidates associated with the address.
|
|
3808
|
-
*/
|
|
3809
|
-
async findByAddress(address) {
|
|
3810
|
-
const trimmedAddress = address.trim();
|
|
3811
|
-
if (!trimmedAddress) {
|
|
3812
|
-
throw new LiquidiumError(
|
|
3813
|
-
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
3814
|
-
"Address lookup requires a non-empty address"
|
|
3815
|
-
);
|
|
3816
|
-
}
|
|
3817
|
-
const apiClient = this.requireApi("Instant loan address lookup");
|
|
3863
|
+
async findCandidateLoansByQuery(query) {
|
|
3864
|
+
const apiClient = this.requireApi("Instant loan find");
|
|
3818
3865
|
const response = await apiClient.get(
|
|
3819
|
-
|
|
3866
|
+
buildInstantLoanFindPath({ query })
|
|
3820
3867
|
);
|
|
3821
3868
|
return (response.candidates ?? response.loans ?? []).map(mapCandidateWire);
|
|
3822
3869
|
}
|
|
3870
|
+
async getLoanRecord(loanId) {
|
|
3871
|
+
try {
|
|
3872
|
+
const result = await createInstantLoansActor(
|
|
3873
|
+
this.canisterContext
|
|
3874
|
+
).get_loan(loanId);
|
|
3875
|
+
if ("Err" in result) {
|
|
3876
|
+
throw mapInstantLoansErrorToLiquidiumError(result.Err);
|
|
3877
|
+
}
|
|
3878
|
+
return result.Ok;
|
|
3879
|
+
} catch (error) {
|
|
3880
|
+
if (error instanceof LiquidiumError) {
|
|
3881
|
+
throw error;
|
|
3882
|
+
}
|
|
3883
|
+
throw mapCanisterCallErrorToLiquidiumError("get_loan", error);
|
|
3884
|
+
}
|
|
3885
|
+
}
|
|
3823
3886
|
async mapLoanRecord(record, collateralAmount) {
|
|
3824
3887
|
return await this.hydrateLoan({
|
|
3825
3888
|
loanId: record.id,
|
|
@@ -3833,37 +3896,22 @@ var InstantLoansModule = class {
|
|
|
3833
3896
|
borrowAsset: getVariantKey(record.borrow_asset),
|
|
3834
3897
|
borrowAmount: record.borrow_amount,
|
|
3835
3898
|
borrowDestination: accountFromCanister(record.borrow_destination),
|
|
3836
|
-
refundDestination: accountFromCanister(record.refund_destination)
|
|
3899
|
+
refundDestination: accountFromCanister(record.refund_destination),
|
|
3900
|
+
depositDetectedTimestamp: record.deposit_detected_ts[0] ?? null,
|
|
3901
|
+
expiryTimestamp: record.expires_at[0] ?? deriveDepositExpiryTimestamp({
|
|
3902
|
+
depositDetectedTimestamp: record.deposit_detected_ts[0] ?? null,
|
|
3903
|
+
depositWindowSeconds: record.ltv_timer_s
|
|
3904
|
+
})
|
|
3837
3905
|
});
|
|
3838
3906
|
}
|
|
3839
3907
|
async getCollateralAmountHint(loanId) {
|
|
3840
|
-
const apiClient = this.requireApi("Instant loan
|
|
3908
|
+
const apiClient = this.requireApi("Instant loan collateral hint");
|
|
3841
3909
|
const response = await apiClient.get(
|
|
3842
3910
|
buildInstantLoanCollateralHintPath({ loanId })
|
|
3843
3911
|
);
|
|
3844
|
-
return
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
const loanId = parseBigintWire(loan.loanId, "loan ID");
|
|
3848
|
-
return await this.hydrateLoan({
|
|
3849
|
-
loanId,
|
|
3850
|
-
profileId: loan.profileId,
|
|
3851
|
-
ltvMaxBps: parseBigintWire(loan.ltvMaxBps, "max LTV"),
|
|
3852
|
-
depositWindowSeconds: parseBigintWire(
|
|
3853
|
-
loan.depositWindowSeconds,
|
|
3854
|
-
"deposit window"
|
|
3855
|
-
),
|
|
3856
|
-
collateralPoolId: loan.collateral.poolId,
|
|
3857
|
-
collateralAmount: parseBigintWire(
|
|
3858
|
-
loan.collateral.amountHint,
|
|
3859
|
-
"collateral amount"
|
|
3860
|
-
),
|
|
3861
|
-
borrowPoolId: loan.borrow.poolId,
|
|
3862
|
-
collateralAsset: loan.collateral.asset,
|
|
3863
|
-
borrowAsset: loan.borrow.asset,
|
|
3864
|
-
borrowAmount: parseBigintWire(loan.borrow.amount, "borrow amount"),
|
|
3865
|
-
borrowDestination: accountFromWire(loan.borrow.destination),
|
|
3866
|
-
refundDestination: accountFromWire(loan.refundDestination)
|
|
3912
|
+
return parseUnsignedApiBigint(response.collateralAmountHint, {
|
|
3913
|
+
context: INSTANT_LOAN_WIRE_CONTEXT,
|
|
3914
|
+
label: "collateral amount"
|
|
3867
3915
|
});
|
|
3868
3916
|
}
|
|
3869
3917
|
async hydrateLoan(input) {
|
|
@@ -3917,7 +3965,9 @@ var InstantLoansModule = class {
|
|
|
3917
3965
|
collateralAmount,
|
|
3918
3966
|
decimals: collateralDecimals,
|
|
3919
3967
|
asset: collateralAsset,
|
|
3920
|
-
target: depositTarget
|
|
3968
|
+
target: depositTarget,
|
|
3969
|
+
detectedTimestamp: input.depositDetectedTimestamp,
|
|
3970
|
+
expiryTimestamp: input.expiryTimestamp
|
|
3921
3971
|
});
|
|
3922
3972
|
const repayment = {
|
|
3923
3973
|
amount: repaymentAmount,
|
|
@@ -3981,7 +4031,9 @@ var InstantLoansModule = class {
|
|
|
3981
4031
|
inflowFeeAmount: inflowFee.totalFee,
|
|
3982
4032
|
asset: input.asset,
|
|
3983
4033
|
chain: input.target.chain,
|
|
3984
|
-
target: input.target
|
|
4034
|
+
target: input.target,
|
|
4035
|
+
detectedTimestamp: input.detectedTimestamp,
|
|
4036
|
+
expiryTimestamp: input.expiryTimestamp
|
|
3985
4037
|
};
|
|
3986
4038
|
}
|
|
3987
4039
|
async estimateRepaymentInflowFee(asset, chain) {
|
|
@@ -4077,6 +4129,12 @@ function deriveInstantLoanStatus(input) {
|
|
|
4077
4129
|
}
|
|
4078
4130
|
return InstantLoanStatus.awaitingDeposit;
|
|
4079
4131
|
}
|
|
4132
|
+
function deriveDepositExpiryTimestamp(input) {
|
|
4133
|
+
if (input.depositDetectedTimestamp === null) {
|
|
4134
|
+
return null;
|
|
4135
|
+
}
|
|
4136
|
+
return input.depositDetectedTimestamp + input.depositWindowSeconds;
|
|
4137
|
+
}
|
|
4080
4138
|
function validateCreateRequest(request) {
|
|
4081
4139
|
if (request.collateralAmount <= 0n) {
|
|
4082
4140
|
throw new LiquidiumError(
|
|
@@ -4125,18 +4183,6 @@ function accountFromCanister(account) {
|
|
|
4125
4183
|
}
|
|
4126
4184
|
return { type: "External", address: account.External };
|
|
4127
4185
|
}
|
|
4128
|
-
function accountFromWire(account) {
|
|
4129
|
-
if ("Native" in account) {
|
|
4130
|
-
return { type: "Native", principal: account.Native };
|
|
4131
|
-
}
|
|
4132
|
-
if ("External" in account) {
|
|
4133
|
-
return { type: "External", address: account.External };
|
|
4134
|
-
}
|
|
4135
|
-
if (account.type === "Native") {
|
|
4136
|
-
return { type: "Native", principal: account.principal };
|
|
4137
|
-
}
|
|
4138
|
-
return { type: "External", address: account.address };
|
|
4139
|
-
}
|
|
4140
4186
|
function mapInstantLoanEvent(event) {
|
|
4141
4187
|
return {
|
|
4142
4188
|
id: event.id,
|
|
@@ -4255,55 +4301,80 @@ function decodeRef(ref) {
|
|
|
4255
4301
|
);
|
|
4256
4302
|
}
|
|
4257
4303
|
}
|
|
4258
|
-
function
|
|
4259
|
-
|
|
4260
|
-
const ref = wire.ref ?? wire.short_ref ?? publicIdFromInt(loanId);
|
|
4261
|
-
const createdAt = wire.createdAt ?? wire.created_at;
|
|
4262
|
-
return {
|
|
4263
|
-
loanId,
|
|
4264
|
-
ref,
|
|
4265
|
-
profileId: requiredString(
|
|
4266
|
-
wire.profileId ?? wire.lending_profile,
|
|
4267
|
-
"profile ID"
|
|
4268
|
-
),
|
|
4269
|
-
...createdAt ? { createdAt: new Date(createdAt) } : {},
|
|
4270
|
-
collateralPoolId: requiredString(
|
|
4271
|
-
wire.collateralPoolId ?? wire.lend_pool_ic_id,
|
|
4272
|
-
"collateral pool ID"
|
|
4273
|
-
),
|
|
4274
|
-
borrowPoolId: requiredString(
|
|
4275
|
-
wire.borrowPoolId ?? wire.borrow_pool_ic_id,
|
|
4276
|
-
"borrow pool ID"
|
|
4277
|
-
),
|
|
4278
|
-
collateralAsset: requiredString(
|
|
4279
|
-
wire.collateralAsset ?? wire.lend_asset,
|
|
4280
|
-
"collateral asset"
|
|
4281
|
-
),
|
|
4282
|
-
borrowAsset: requiredString(
|
|
4283
|
-
wire.borrowAsset ?? wire.borrow_asset,
|
|
4284
|
-
"borrow asset"
|
|
4285
|
-
),
|
|
4286
|
-
collateralAmount: parseBigintWire(
|
|
4287
|
-
wire.collateralAmount,
|
|
4288
|
-
"collateral amount"
|
|
4289
|
-
)
|
|
4290
|
-
};
|
|
4291
|
-
}
|
|
4292
|
-
function parseBigintWire(value, label) {
|
|
4293
|
-
if (!value || !/^\d+$/.test(value)) {
|
|
4304
|
+
function validateInstantLoanFindQuery(query) {
|
|
4305
|
+
if (typeof query !== "string") {
|
|
4294
4306
|
throw new LiquidiumError(
|
|
4295
4307
|
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
4296
|
-
|
|
4308
|
+
"Instant loan find query must be a string"
|
|
4297
4309
|
);
|
|
4298
4310
|
}
|
|
4299
|
-
|
|
4311
|
+
const trimmedQuery = query.trim();
|
|
4312
|
+
if (!trimmedQuery) {
|
|
4313
|
+
throw new LiquidiumError(
|
|
4314
|
+
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
4315
|
+
"Instant loan find query must be non-empty"
|
|
4316
|
+
);
|
|
4317
|
+
}
|
|
4318
|
+
if (trimmedQuery.length > INSTANT_LOAN_FIND_QUERY_MAX_LENGTH) {
|
|
4319
|
+
throw new LiquidiumError(
|
|
4320
|
+
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
4321
|
+
`Instant loan find query must be at most ${INSTANT_LOAN_FIND_QUERY_MAX_LENGTH.toString()} characters`
|
|
4322
|
+
);
|
|
4323
|
+
}
|
|
4324
|
+
return trimmedQuery;
|
|
4300
4325
|
}
|
|
4301
|
-
function
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4326
|
+
function uniqueInstantLoanFindCandidates(candidates) {
|
|
4327
|
+
const candidatesByLoanId = /* @__PURE__ */ new Map();
|
|
4328
|
+
for (const candidate of candidates) {
|
|
4329
|
+
if (!candidatesByLoanId.has(candidate.loanId)) {
|
|
4330
|
+
candidatesByLoanId.set(candidate.loanId, candidate);
|
|
4331
|
+
}
|
|
4332
|
+
}
|
|
4333
|
+
return [...candidatesByLoanId.values()];
|
|
4334
|
+
}
|
|
4335
|
+
function mapCandidateWire(wire) {
|
|
4336
|
+
return {
|
|
4337
|
+
loanId: parseUnsignedApiBigint(wire.loan_id, {
|
|
4338
|
+
context: INSTANT_LOAN_WIRE_CONTEXT,
|
|
4339
|
+
label: "loan ID"
|
|
4340
|
+
}),
|
|
4341
|
+
ref: parseNonEmptyApiString(wire.short_ref, {
|
|
4342
|
+
context: INSTANT_LOAN_WIRE_CONTEXT,
|
|
4343
|
+
label: "short reference"
|
|
4344
|
+
}),
|
|
4345
|
+
createdAt: parseIsoApiTimestampToUnixSeconds(wire.created_at, {
|
|
4346
|
+
context: INSTANT_LOAN_WIRE_CONTEXT,
|
|
4347
|
+
label: "creation timestamp"
|
|
4348
|
+
}),
|
|
4349
|
+
collateral: {
|
|
4350
|
+
poolId: parseNonEmptyApiString(wire.lend_pool_ic_id, {
|
|
4351
|
+
context: INSTANT_LOAN_WIRE_CONTEXT,
|
|
4352
|
+
label: "lend pool ID"
|
|
4353
|
+
}),
|
|
4354
|
+
asset: parseApiStringUnion(wire.lend_asset, INSTANT_LOAN_ASSETS, {
|
|
4355
|
+
context: INSTANT_LOAN_WIRE_CONTEXT,
|
|
4356
|
+
label: "lend asset"
|
|
4357
|
+
}),
|
|
4358
|
+
amount: parseUnsignedApiBigint(wire.collateral_amount, {
|
|
4359
|
+
context: INSTANT_LOAN_WIRE_CONTEXT,
|
|
4360
|
+
label: "collateral amount"
|
|
4361
|
+
})
|
|
4362
|
+
},
|
|
4363
|
+
borrow: {
|
|
4364
|
+
poolId: parseNonEmptyApiString(wire.borrow_pool_ic_id, {
|
|
4365
|
+
context: INSTANT_LOAN_WIRE_CONTEXT,
|
|
4366
|
+
label: "borrow pool ID"
|
|
4367
|
+
}),
|
|
4368
|
+
asset: parseApiStringUnion(wire.borrow_asset, INSTANT_LOAN_ASSETS, {
|
|
4369
|
+
context: INSTANT_LOAN_WIRE_CONTEXT,
|
|
4370
|
+
label: "borrow asset"
|
|
4371
|
+
})
|
|
4372
|
+
},
|
|
4373
|
+
profileId: parseNonEmptyApiString(wire.profile, {
|
|
4374
|
+
context: INSTANT_LOAN_WIRE_CONTEXT,
|
|
4375
|
+
label: "profile ID"
|
|
4376
|
+
})
|
|
4377
|
+
};
|
|
4307
4378
|
}
|
|
4308
4379
|
function mapInstantLoansErrorToLiquidiumError(error) {
|
|
4309
4380
|
const [key, payload] = Object.entries(error)[0];
|
|
@@ -5908,7 +5979,7 @@ var PositionsModule = class {
|
|
|
5908
5979
|
pool,
|
|
5909
5980
|
priceUsd,
|
|
5910
5981
|
suppliedUsd: nativeAmountToUsdScaled(
|
|
5911
|
-
position.deposited
|
|
5982
|
+
position.deposited,
|
|
5912
5983
|
position.depositedDecimals,
|
|
5913
5984
|
priceUsd
|
|
5914
5985
|
),
|
|
@@ -5943,6 +6014,24 @@ var PositionsModule = class {
|
|
|
5943
6014
|
const buffered = rawDebt * (BPS_SCALE + bufferBps) / BPS_SCALE;
|
|
5944
6015
|
return { amount: buffered, decimals: position.borrowedDecimals };
|
|
5945
6016
|
}
|
|
6017
|
+
/**
|
|
6018
|
+
* Returns the current full withdraw amount for a position.
|
|
6019
|
+
*
|
|
6020
|
+
* `Position.deposited` already reflects the current supplied balance at the
|
|
6021
|
+
* latest lending index; do not add `earnedInterest` to this amount.
|
|
6022
|
+
* Pass `amount` to withdraw calls and use `decimals` for display formatting.
|
|
6023
|
+
*
|
|
6024
|
+
* @param profileId - The Liquidium profile principal text.
|
|
6025
|
+
* @param poolId - The pool principal text.
|
|
6026
|
+
* @returns Full withdraw amount in the supplied asset's base units.
|
|
6027
|
+
*/
|
|
6028
|
+
async getFullWithdrawAmount(profileId, poolId) {
|
|
6029
|
+
const position = await this.getPosition(profileId, poolId);
|
|
6030
|
+
if (!position) {
|
|
6031
|
+
return { amount: 0n, decimals: 0n };
|
|
6032
|
+
}
|
|
6033
|
+
return { amount: position.deposited, decimals: position.depositedDecimals };
|
|
6034
|
+
}
|
|
5946
6035
|
};
|
|
5947
6036
|
function nativeAmountToUsdScaled(amount, nativeDecimals, priceUsd) {
|
|
5948
6037
|
if (amount <= 0n || priceUsd <= 0) {
|