@kasufinance/kasu-sdk 2.7.0 → 2.8.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 +88 -6
- package/dist/bundle.cjs.js +541 -21
- package/dist/bundle.esm.js +537 -22
- package/dist/domain/index.d.ts +2 -0
- package/dist/domain/index.js +1 -0
- package/dist/domain/index.js.map +1 -1
- package/dist/domain/revert-errors.d.ts +59 -0
- package/dist/domain/revert-errors.js +190 -0
- package/dist/domain/revert-errors.js.map +1 -0
- package/dist/facade/flows.d.ts +17 -1
- package/dist/facade/flows.js +15 -2
- package/dist/facade/flows.js.map +1 -1
- package/dist/facade/kasu.js +2 -1
- package/dist/facade/kasu.js.map +1 -1
- package/dist/flows/deposit-flow.d.ts +170 -8
- package/dist/flows/deposit-flow.js +333 -15
- package/dist/flows/deposit-flow.js.map +1 -1
- package/dist/flows/index.d.ts +2 -2
- package/dist/flows/index.js +1 -1
- package/dist/flows/index.js.map +1 -1
- package/package.json +1 -1
- package/src/domain/index.ts +3 -0
- package/src/domain/revert-errors.ts +248 -0
- package/src/facade/flows.ts +12 -0
- package/src/facade/kasu.ts +1 -0
- package/src/flows/deposit-flow.ts +509 -22
- package/src/flows/index.ts +4 -0
|
@@ -45,16 +45,41 @@ import { WaitableTransaction } from './observable';
|
|
|
45
45
|
* 8. **`request-sign` / `request-confirm`** — KYC signature, then the deposit,
|
|
46
46
|
* then the receipt.
|
|
47
47
|
*
|
|
48
|
+
* ## The batch path
|
|
49
|
+
*
|
|
50
|
+
* A consumer that can submit several calls as ONE atomic transaction supplies
|
|
51
|
+
* the optional `depositBatch` port. When the allowance is short, that port is
|
|
52
|
+
* present and the stable token is known, steps 7 and 8 become one: the SDK
|
|
53
|
+
* encodes `approve(spender, amount)` and `requestDepositWithKyc(...)` itself
|
|
54
|
+
* and hands both to the port. Because a batch has no wallet prompt per call,
|
|
55
|
+
* the run first PARKS on `awaiting-authorise` until the consumer calls
|
|
56
|
+
* `authoriseTransfer()` (or `declineTransfer()`) — nothing moves money
|
|
57
|
+
* without an explicit authorisation. The TTL is checked again after it, and
|
|
58
|
+
* the KYC signature is fetched only then, because it carries a block
|
|
59
|
+
* expiration of its own. Without the port, or with the allowance already
|
|
60
|
+
* sufficient, the run is exactly the two-transaction pipeline above.
|
|
61
|
+
*
|
|
48
62
|
* ## Failure codes
|
|
49
63
|
*
|
|
50
64
|
* A wallet rejection (`isUserRejected`) is `'cancelled'` — the lender changed
|
|
51
65
|
* their mind, and telling them something broke would be a lie. Only a WALLET
|
|
52
66
|
* call is ever classified that way: the HTTP ports are always `'failed'`,
|
|
53
67
|
* because a backend that words a refusal "declined" did not involve the
|
|
54
|
-
* lender's wallet. A
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
68
|
+
* lender's wallet. A revert on the request step is read for WHAT reverted: a
|
|
69
|
+
* protocol error the ABI declares is `'reverted'` with the name, and only a
|
|
70
|
+
* token balance or allowance failure — or a revert nothing can decode — stays
|
|
71
|
+
* `'insufficient-balance'`. Everything else is `'failed'` and carries the
|
|
72
|
+
* original error for the consumer's crash reporter.
|
|
73
|
+
*
|
|
74
|
+
* ## Both signatures are checked before they are used
|
|
75
|
+
*
|
|
76
|
+
* A wallet is not obliged to hand back 65 bytes, and `encodeDepositData` will
|
|
77
|
+
* not catch one that does not: `defaultAbiCoder` encodes any even-length hex
|
|
78
|
+
* as `bytes`, `0x` included, so a malformed acceptance signature becomes a
|
|
79
|
+
* `depositData` blob that is broadcast, mined, and then can never be verified
|
|
80
|
+
* by the agreements service. Both personal signs are therefore length-checked
|
|
81
|
+
* where they are taken, and a bad one fails the step it was taken on rather
|
|
82
|
+
* than the one it would eventually have broken.
|
|
58
83
|
*
|
|
59
84
|
* ```ts
|
|
60
85
|
* const flow = new DepositFlow(ports);
|
|
@@ -68,7 +93,12 @@ import { WaitableTransaction } from './observable';
|
|
|
68
93
|
* Where the run is. `success`, `declined` and `error` are terminal; everything
|
|
69
94
|
* else is in flight.
|
|
70
95
|
*/
|
|
71
|
-
export type DepositPhase = 'idle' | 'generating-sign' | 'generating-fetch' | 'awaiting-accept' | 'accepting-sign' | 'approve'
|
|
96
|
+
export type DepositPhase = 'idle' | 'generating-sign' | 'generating-fetch' | 'awaiting-accept' | 'accepting-sign' | 'approve'
|
|
97
|
+
/**
|
|
98
|
+
* Batch path only: the run is PARKED until `authoriseTransfer()` or
|
|
99
|
+
* `declineTransfer()`. Step `request`.
|
|
100
|
+
*/
|
|
101
|
+
| 'awaiting-authorise' | 'request-sign' | 'request-confirm' | 'success' | 'declined' | 'error';
|
|
72
102
|
/**
|
|
73
103
|
* The four steps a lender sees as a badge. `approve` drops out of the sequence
|
|
74
104
|
* when the allowance already covers the deposit, which is why `stepIndex` and
|
|
@@ -93,11 +123,55 @@ export type DepositFailure = {
|
|
|
93
123
|
step: 'request';
|
|
94
124
|
reason: 'insufficient-balance';
|
|
95
125
|
error: unknown;
|
|
126
|
+
} | {
|
|
127
|
+
step: 'request';
|
|
128
|
+
reason: 'reverted';
|
|
129
|
+
/**
|
|
130
|
+
* The custom error the contract reverted with, exactly as the ABI
|
|
131
|
+
* declares it — `'ClearingIsPending'`, `'LendingPoolIsStopped'`,
|
|
132
|
+
* `'UserNotKycd'`, and the rest of
|
|
133
|
+
* `ILendingPoolManagerAbi` / `IKasuAllowListAbi`.
|
|
134
|
+
*
|
|
135
|
+
* Render `reverted` with generic copy and special-case only the
|
|
136
|
+
* names you have words for: a contract upgrade can add an error, and
|
|
137
|
+
* a consumer that assumed the set was closed would have nothing to
|
|
138
|
+
* show for the new one.
|
|
139
|
+
*/
|
|
140
|
+
revertError: string;
|
|
141
|
+
error: unknown;
|
|
96
142
|
} | {
|
|
97
143
|
step: 'request';
|
|
98
144
|
reason: 'contract-expired';
|
|
99
145
|
};
|
|
100
146
|
export type { WaitableTransaction };
|
|
147
|
+
/**
|
|
148
|
+
* One call of an atomic batch: a target and its ABI-encoded calldata. No
|
|
149
|
+
* value — neither call the deposit batch makes sends the native token.
|
|
150
|
+
*/
|
|
151
|
+
export interface BatchCall {
|
|
152
|
+
to: string;
|
|
153
|
+
data: string;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* What a `depositBatch` port throws when it KNOWS the batch reverted — from a
|
|
157
|
+
* simulation before sending, or from the submitter refusing to broadcast a
|
|
158
|
+
* batch that would revert. The run ends `{ step: 'request', reason:
|
|
159
|
+
* 'reverted', revertError }`.
|
|
160
|
+
*
|
|
161
|
+
* `revertError` is the custom error the contract named (`'ClearingIsPending'`,
|
|
162
|
+
* `'UserNotKycd'`, …), exactly as the ABI declares it. Pass `''` when the
|
|
163
|
+
* batch is known to revert but not why: the consumer then renders its generic
|
|
164
|
+
* `reverted` copy, which is what an unknown name gets anyway.
|
|
165
|
+
*
|
|
166
|
+
* Any OTHER throw from the port is read the way a direct deposit's is — a
|
|
167
|
+
* wallet rejection is `cancelled`, a decodable revert is `reverted`, and
|
|
168
|
+
* everything else is `failed` with the error kept. A port that cannot tell is
|
|
169
|
+
* better off throwing what it has than guessing a name.
|
|
170
|
+
*/
|
|
171
|
+
export declare class BatchRevertedError extends Error {
|
|
172
|
+
readonly revertError: string;
|
|
173
|
+
constructor(revertError: string, message?: string);
|
|
174
|
+
}
|
|
101
175
|
/** What the KYC signing service hands back. */
|
|
102
176
|
export interface KycSignature {
|
|
103
177
|
signature: string;
|
|
@@ -190,10 +264,34 @@ export interface DepositPorts {
|
|
|
190
264
|
approve(spender: string, amount: BigNumber): Promise<WaitableTransaction>;
|
|
191
265
|
/** `requestDepositWithKyc`. Defaults to `kasu.deposits.deposit`. */
|
|
192
266
|
deposit(params: DepositParams): Promise<WaitableTransaction>;
|
|
267
|
+
/**
|
|
268
|
+
* OPTIONAL, and with no default: submit several calls as ONE atomic
|
|
269
|
+
* transaction — all of them apply, or none does. How is the consumer's
|
|
270
|
+
* business; the SDK owns what goes in and in which order.
|
|
271
|
+
*
|
|
272
|
+
* When supplied, the allowance is short and the flow knows the stable
|
|
273
|
+
* token (`DepositFlowOptions.stableAsset`), the run takes the batch path:
|
|
274
|
+
* it parks on `awaiting-authorise`, then hands this port exactly two calls
|
|
275
|
+
* —
|
|
276
|
+
*
|
|
277
|
+
* 0. `stableAsset.approve(spender, amount)` — the EXACT deposit amount,
|
|
278
|
+
* never `MaxUint256`;
|
|
279
|
+
* 1. `spender.requestDepositWithKyc(pool, tranche, amount, '0x',
|
|
280
|
+
* fixedTermConfigId, depositData, { blockExpiration, signature })` —
|
|
281
|
+
* the call the default `deposit` port makes.
|
|
282
|
+
*
|
|
283
|
+
* and neither `approve` nor `deposit` is called. Resolve with something
|
|
284
|
+
* whose `wait()` settles when the batch is mined. Throw
|
|
285
|
+
* `BatchRevertedError` when the batch is known to revert.
|
|
286
|
+
*/
|
|
287
|
+
depositBatch?(calls: BatchCall[]): Promise<WaitableTransaction>;
|
|
193
288
|
/** ms-epoch clock. Defaults to `Date.now`; injected so the TTL is testable. */
|
|
194
289
|
now?(): number;
|
|
195
290
|
}
|
|
196
|
-
/**
|
|
291
|
+
/**
|
|
292
|
+
* Construction options. `kasu.flows.deposit()` fills `spender` and
|
|
293
|
+
* `stableAsset` in.
|
|
294
|
+
*/
|
|
197
295
|
export interface DepositFlowOptions {
|
|
198
296
|
/** Agreement validity window; defaults to `CONTRACT_TTL_MS`. */
|
|
199
297
|
contractTtlMs?: number;
|
|
@@ -204,6 +302,17 @@ export interface DepositFlowOptions {
|
|
|
204
302
|
* deposit port calls.
|
|
205
303
|
*/
|
|
206
304
|
spender?: string;
|
|
305
|
+
/**
|
|
306
|
+
* The ERC-20 the deposit is paid in — the target of the batch path's
|
|
307
|
+
* approve call. `kasu.flows.deposit()` passes this chain's
|
|
308
|
+
* `stableAsset.address`.
|
|
309
|
+
*
|
|
310
|
+
* Without it the batch path is never taken, even when `depositBatch` is
|
|
311
|
+
* supplied: an approve encoded against a guessed token is an approval
|
|
312
|
+
* granted on the wrong contract inside a transaction that then reverts.
|
|
313
|
+
* The run falls back to the `approve` and `deposit` ports instead.
|
|
314
|
+
*/
|
|
315
|
+
stableAsset?: string;
|
|
207
316
|
}
|
|
208
317
|
export interface DepositFlowInput {
|
|
209
318
|
poolId: string;
|
|
@@ -241,10 +350,24 @@ export interface DepositState {
|
|
|
241
350
|
step: DepositStep | null;
|
|
242
351
|
/** 1-based badge position of `step`; `0` while idle. */
|
|
243
352
|
stepIndex: number;
|
|
244
|
-
/**
|
|
353
|
+
/**
|
|
354
|
+
* `4`, or `3` when the allowance already covers the deposit or the run
|
|
355
|
+
* is batched.
|
|
356
|
+
*/
|
|
245
357
|
stepTotal: number;
|
|
246
|
-
/**
|
|
358
|
+
/**
|
|
359
|
+
* Whether the separate approve STEP is in scope for this run. `false`
|
|
360
|
+
* on a batched run too: its approval rides inside the request step's
|
|
361
|
+
* single transaction, so it never enters `approve` and counts as a
|
|
362
|
+
* three-step run on every badge.
|
|
363
|
+
*/
|
|
247
364
|
approvalRequired: boolean;
|
|
365
|
+
/**
|
|
366
|
+
* Whether this run approves and deposits as ONE atomic batch through the
|
|
367
|
+
* `depositBatch` port. Decided by the allowance pre-check, before the
|
|
368
|
+
* first signature, and `false` on every two-transaction run.
|
|
369
|
+
*/
|
|
370
|
+
batched: boolean;
|
|
248
371
|
/** The generated agreement, from `generating-fetch` onwards. */
|
|
249
372
|
contract: GenerateContractResponse | null;
|
|
250
373
|
/** Set with `phase: 'error'`, cleared by `reset()`. */
|
|
@@ -254,10 +377,15 @@ export interface DepositState {
|
|
|
254
377
|
export declare const CONTRACT_TTL_MS: number;
|
|
255
378
|
/** What a run without a spender, from either source, fails with. */
|
|
256
379
|
export declare const NO_SPENDER_MESSAGE = "DepositFlow: no ERC-20 spender; build the flow with kasu.flows.deposit() or pass `spender` on the input";
|
|
380
|
+
/** What a malformed auth signature fails the generate step with. */
|
|
381
|
+
export declare const INVALID_AUTH_SIGNATURE_MESSAGE = "DepositFlow: the wallet returned a malformed authentication signature; expected 65 bytes of 0x-prefixed hex";
|
|
382
|
+
/** What a malformed acceptance signature fails the confirm step with. */
|
|
383
|
+
export declare const INVALID_ACCEPTANCE_SIGNATURE_MESSAGE = "DepositFlow: the wallet returned a malformed acceptance signature; expected 65 bytes of 0x-prefixed hex";
|
|
257
384
|
export declare class DepositFlow extends Flow<DepositState, DepositFlowInput> {
|
|
258
385
|
private readonly _ports;
|
|
259
386
|
private readonly _ttlMs;
|
|
260
387
|
private readonly _defaultSpender;
|
|
388
|
+
private readonly _stableAsset;
|
|
261
389
|
private readonly _now;
|
|
262
390
|
/**
|
|
263
391
|
* The accept handshake. The run parks on this promise; `acceptContract`,
|
|
@@ -277,6 +405,12 @@ export declare class DepositFlow extends Flow<DepositState, DepositFlowInput> {
|
|
|
277
405
|
* A stale token simply is not the current generation.
|
|
278
406
|
*/
|
|
279
407
|
private _acceptingFor;
|
|
408
|
+
/**
|
|
409
|
+
* The batch path's authorisation handshake. Same shape and lifetime as
|
|
410
|
+
* `_accept`: the run parks on it, `authoriseTransfer`, `declineTransfer`
|
|
411
|
+
* and `reset` settle it, and it is cleared the moment it settles.
|
|
412
|
+
*/
|
|
413
|
+
private _authorise;
|
|
280
414
|
constructor(_ports: DepositPorts, opts?: DepositFlowOptions);
|
|
281
415
|
/**
|
|
282
416
|
* Sign the agreement and resume the parked run. A no-op when nothing is
|
|
@@ -292,10 +426,38 @@ export declare class DepositFlow extends Flow<DepositState, DepositFlowInput> {
|
|
|
292
426
|
* out of a prompt that never answers.
|
|
293
427
|
*/
|
|
294
428
|
declineContract(): void;
|
|
429
|
+
/**
|
|
430
|
+
* Batch path: authorise the single transaction that approves and deposits,
|
|
431
|
+
* and resume the parked run. A no-op when nothing is parked on
|
|
432
|
+
* `awaiting-authorise`, so a double tap cannot submit twice.
|
|
433
|
+
*
|
|
434
|
+
* The batch has no wallet prompt per call, so this is the lender's
|
|
435
|
+
* explicit consent to the transfer — call it only from their action.
|
|
436
|
+
*/
|
|
437
|
+
authoriseTransfer(): void;
|
|
438
|
+
/**
|
|
439
|
+
* Batch path: refuse the transfer. The run ends on `error` with `{ step:
|
|
440
|
+
* 'request', reason: 'cancelled' }` and nothing is submitted. A no-op when
|
|
441
|
+
* nothing is parked on `awaiting-authorise`.
|
|
442
|
+
*/
|
|
443
|
+
declineTransfer(): void;
|
|
295
444
|
/** True only while THIS generation is waiting on the acceptance signature. */
|
|
296
445
|
private _isAccepting;
|
|
297
446
|
/** `reset()`: unpark the abandoned run and drop its handshake. */
|
|
298
447
|
protected _onAbandon(): void;
|
|
299
448
|
protected _run(input: DepositFlowInput, token: number): Promise<void>;
|
|
449
|
+
/**
|
|
450
|
+
* The batch path, from the accepted agreement to the receipt.
|
|
451
|
+
*
|
|
452
|
+
* 1. PARK on `awaiting-authorise`. A batch puts no wallet prompt in front
|
|
453
|
+
* of each call, so the flow waits for an explicit authorisation before
|
|
454
|
+
* any money moves. `declineTransfer()` ends it as `cancelled`.
|
|
455
|
+
* 2. Re-check the TTL: the lender may have idled on the authorisation as
|
|
456
|
+
* long as they did on the agreement.
|
|
457
|
+
* 3. Only now fetch the KYC signature — it has a block expiration of its
|
|
458
|
+
* own, and an authorisation left open must not spend it.
|
|
459
|
+
* 4. Encode both calls and hand them to `depositBatch`; then the receipt.
|
|
460
|
+
*/
|
|
461
|
+
private _runBatch;
|
|
300
462
|
private _fail;
|
|
301
463
|
}
|