@kasufinance/kasu-sdk 2.6.0 → 2.7.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 +128 -3
- package/dist/bundle.cjs.js +1119 -254
- package/dist/bundle.esm.js +1109 -255
- package/dist/domain/index.d.ts +3 -2
- package/dist/domain/index.js +2 -2
- package/dist/domain/index.js.map +1 -1
- package/dist/domain/requests.d.ts +42 -4
- package/dist/domain/requests.js +20 -4
- package/dist/domain/requests.js.map +1 -1
- package/dist/domain/wallet-errors.d.ts +63 -8
- package/dist/domain/wallet-errors.js +115 -16
- package/dist/domain/wallet-errors.js.map +1 -1
- package/dist/facade/deposits.js +2 -1
- package/dist/facade/deposits.js.map +1 -1
- package/dist/facade/flows.d.ts +95 -0
- package/dist/facade/flows.js +116 -0
- package/dist/facade/flows.js.map +1 -0
- package/dist/facade/index.d.ts +3 -0
- package/dist/facade/index.js +3 -0
- package/dist/facade/index.js.map +1 -1
- package/dist/facade/kasu.d.ts +6 -2
- package/dist/facade/kasu.js +28 -3
- package/dist/facade/kasu.js.map +1 -1
- package/dist/facade/read-only.d.ts +12 -0
- package/dist/facade/read-only.js +13 -0
- package/dist/facade/read-only.js.map +1 -0
- package/dist/flows/deposit-flow.d.ts +301 -0
- package/dist/flows/deposit-flow.js +358 -0
- package/dist/flows/deposit-flow.js.map +1 -0
- package/dist/flows/flow.d.ts +72 -0
- package/dist/flows/flow.js +110 -0
- package/dist/flows/flow.js.map +1 -0
- package/dist/flows/index.d.ts +19 -0
- package/dist/flows/index.js +17 -0
- package/dist/flows/index.js.map +1 -0
- package/dist/flows/observable.d.ts +59 -0
- package/dist/flows/observable.js +81 -0
- package/dist/flows/observable.js.map +1 -0
- package/dist/flows/withdraw-flow.d.ts +99 -0
- package/dist/flows/withdraw-flow.js +83 -0
- package/dist/flows/withdraw-flow.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/services/DataService/data-service.js +0 -1
- package/dist/services/DataService/data-service.js.map +1 -1
- package/package.json +8 -2
- package/src/domain/index.ts +7 -1
- package/src/domain/requests.ts +57 -4
- package/src/domain/wallet-errors.ts +135 -18
- package/src/facade/deposits.ts +2 -3
- package/src/facade/flows.ts +172 -0
- package/src/facade/index.ts +8 -0
- package/src/facade/kasu.ts +43 -3
- package/src/facade/read-only.ts +13 -0
- package/src/flows/deposit-flow.ts +775 -0
- package/src/flows/flow.ts +108 -0
- package/src/flows/index.ts +45 -0
- package/src/flows/observable.ts +97 -0
- package/src/flows/withdraw-flow.ts +210 -0
- package/src/index.ts +7 -0
- package/src/services/DataService/data-service.ts +0 -1
- package/dist/domain/au-minimum.test.d.ts +0 -1
- package/dist/domain/au-minimum.test.js +0 -202
- package/dist/domain/au-minimum.test.js.map +0 -1
- package/dist/domain/deposit-bounds.test.d.ts +0 -1
- package/dist/domain/deposit-bounds.test.js +0 -135
- package/dist/domain/deposit-bounds.test.js.map +0 -1
- package/dist/domain/loan-contract.test.d.ts +0 -1
- package/dist/domain/loan-contract.test.js +0 -255
- package/dist/domain/loan-contract.test.js.map +0 -1
- package/dist/domain/partners.test.d.ts +0 -1
- package/dist/domain/partners.test.js +0 -53
- package/dist/domain/partners.test.js.map +0 -1
- package/dist/domain/pools.test.d.ts +0 -1
- package/dist/domain/pools.test.js +0 -184
- package/dist/domain/pools.test.js.map +0 -1
- package/dist/domain/rates.test.d.ts +0 -1
- package/dist/domain/rates.test.js +0 -181
- package/dist/domain/rates.test.js.map +0 -1
- package/dist/domain/requests.test.d.ts +0 -1
- package/dist/domain/requests.test.js +0 -470
- package/dist/domain/requests.test.js.map +0 -1
- package/dist/domain/settlement.test.d.ts +0 -1
- package/dist/domain/settlement.test.js +0 -152
- package/dist/domain/settlement.test.js.map +0 -1
- package/dist/domain/tranche-display-name.test.d.ts +0 -1
- package/dist/domain/tranche-display-name.test.js +0 -58
- package/dist/domain/tranche-display-name.test.js.map +0 -1
- package/dist/domain/tranches.test.d.ts +0 -1
- package/dist/domain/tranches.test.js +0 -206
- package/dist/domain/tranches.test.js.map +0 -1
- package/dist/domain/wallet-errors.test.d.ts +0 -1
- package/dist/domain/wallet-errors.test.js +0 -71
- package/dist/domain/wallet-errors.test.js.map +0 -1
- package/dist/facade/config.test.d.ts +0 -1
- package/dist/facade/config.test.js +0 -216
- package/dist/facade/config.test.js.map +0 -1
- package/dist/facade/facade.test.d.ts +0 -1
- package/dist/facade/facade.test.js +0 -278
- package/dist/facade/facade.test.js.map +0 -1
- package/dist/services/Locking/calculate-apy.test.d.ts +0 -1
- package/dist/services/Locking/calculate-apy.test.js +0 -41
- package/dist/services/Locking/calculate-apy.test.js.map +0 -1
- package/dist/tests/sample.test.d.ts +0 -1
- package/dist/tests/sample.test.js +0 -59
- package/dist/tests/sample.test.js.map +0 -1
- package/src/domain/au-minimum.test.ts +0 -371
- package/src/domain/deposit-bounds.test.ts +0 -200
- package/src/domain/loan-contract.test.ts +0 -343
- package/src/domain/partners.test.ts +0 -83
- package/src/domain/pools.test.ts +0 -260
- package/src/domain/rates.test.ts +0 -254
- package/src/domain/requests.test.ts +0 -653
- package/src/domain/settlement.test.ts +0 -198
- package/src/domain/tranche-display-name.test.ts +0 -96
- package/src/domain/tranches.test.ts +0 -300
- package/src/domain/wallet-errors.test.ts +0 -100
- package/src/facade/config.test.ts +0 -265
- package/src/facade/facade.test.ts +0 -374
- package/src/services/Locking/calculate-apy.test.ts +0 -52
- package/src/tests/sample.test.ts +0 -63
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { asContractType, buildLegacyContractRequestMessage, buildLoanAgreementSignMessage, encodeDepositData, } from '../domain/loan-contract';
|
|
11
|
+
import { classifyWalletFailure, isUnpredictableGas, } from '../domain/wallet-errors';
|
|
12
|
+
import { Flow } from './flow';
|
|
13
|
+
/** Generated agreements are valid for five minutes upstream. */
|
|
14
|
+
export const CONTRACT_TTL_MS = 5 * 60 * 1000;
|
|
15
|
+
/** What a run without a spender, from either source, fails with. */
|
|
16
|
+
export const NO_SPENDER_MESSAGE = 'DepositFlow: no ERC-20 spender; build the flow with kasu.flows.deposit() or pass `spender` on the input';
|
|
17
|
+
const INITIAL = {
|
|
18
|
+
phase: 'idle',
|
|
19
|
+
step: null,
|
|
20
|
+
stepIndex: 0,
|
|
21
|
+
stepTotal: 4,
|
|
22
|
+
approvalRequired: true,
|
|
23
|
+
contract: null,
|
|
24
|
+
failure: null,
|
|
25
|
+
};
|
|
26
|
+
/** 1-based badge position, with `approve` dropped when it is out of scope. */
|
|
27
|
+
function stepIndexOf(step, approvalRequired) {
|
|
28
|
+
const order = approvalRequired
|
|
29
|
+
? ['generate', 'confirm', 'approve', 'request']
|
|
30
|
+
: ['generate', 'confirm', 'request'];
|
|
31
|
+
return order.indexOf(step) + 1;
|
|
32
|
+
}
|
|
33
|
+
function badgeFor(step, approvalRequired) {
|
|
34
|
+
return {
|
|
35
|
+
step,
|
|
36
|
+
stepIndex: stepIndexOf(step, approvalRequired),
|
|
37
|
+
stepTotal: approvalRequired ? 4 : 3,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The request step has a third outcome. `UNPREDICTABLE_GAS_LIMIT` here is
|
|
42
|
+
* almost always `transferFrom` reverting on a balance that cannot cover the
|
|
43
|
+
* deposit, and it takes precedence: nothing was refused by the lender, so
|
|
44
|
+
* inviting a retry would just reproduce it.
|
|
45
|
+
*/
|
|
46
|
+
function classifyRequest(err) {
|
|
47
|
+
if (isUnpredictableGas(err)) {
|
|
48
|
+
return { step: 'request', reason: 'insufficient-balance', error: err };
|
|
49
|
+
}
|
|
50
|
+
return classifyWalletFailure('request', err);
|
|
51
|
+
}
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
// The flow
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
export class DepositFlow extends Flow {
|
|
56
|
+
constructor(_ports, opts) {
|
|
57
|
+
var _a;
|
|
58
|
+
super(INITIAL);
|
|
59
|
+
this._ports = _ports;
|
|
60
|
+
/**
|
|
61
|
+
* The accept handshake. The run parks on this promise; `acceptContract`,
|
|
62
|
+
* `declineContract` and `reset` each settle it with an `AcceptOutcome`.
|
|
63
|
+
* Cleared the moment it settles so a stale resolver from an abandoned run
|
|
64
|
+
* can never leak into the next one.
|
|
65
|
+
*/
|
|
66
|
+
this._accept = null;
|
|
67
|
+
/**
|
|
68
|
+
* The run token that is between `acceptContract()` and the wallet
|
|
69
|
+
* settling, or `null`.
|
|
70
|
+
*
|
|
71
|
+
* A token rather than a boolean, because the flag has to belong to the RUN
|
|
72
|
+
* that set it: after `reset()` out of a wallet prompt that never answers,
|
|
73
|
+
* the abandoned run's `finally` may not arrive for minutes, and a boolean
|
|
74
|
+
* left standing refuses both Accept and Decline on every run after it.
|
|
75
|
+
* A stale token simply is not the current generation.
|
|
76
|
+
*/
|
|
77
|
+
this._acceptingFor = null;
|
|
78
|
+
this._ttlMs = (_a = opts === null || opts === void 0 ? void 0 : opts.contractTtlMs) !== null && _a !== void 0 ? _a : CONTRACT_TTL_MS;
|
|
79
|
+
this._defaultSpender = opts === null || opts === void 0 ? void 0 : opts.spender;
|
|
80
|
+
// Called through the ports object, never captured off it: a consumer
|
|
81
|
+
// whose clock is a method on its own object keeps its `this`.
|
|
82
|
+
this._now = () => { var _a, _b; return (_b = (_a = _ports.now) === null || _a === void 0 ? void 0 : _a.call(_ports)) !== null && _b !== void 0 ? _b : Date.now(); };
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Sign the agreement and resume the parked run. A no-op when nothing is
|
|
86
|
+
* parked, so a double tap cannot sign twice.
|
|
87
|
+
*/
|
|
88
|
+
acceptContract() {
|
|
89
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
90
|
+
const bridge = this._accept;
|
|
91
|
+
const contract = this._store.state.contract;
|
|
92
|
+
if (!bridge || !contract || this._isAccepting())
|
|
93
|
+
return;
|
|
94
|
+
const token = this._store.generation;
|
|
95
|
+
this._acceptingFor = token;
|
|
96
|
+
this._store.patch(Object.assign({ phase: 'accepting-sign' }, badgeFor('confirm', this._store.state.approvalRequired)), token);
|
|
97
|
+
try {
|
|
98
|
+
const signature = yield this._ports.signMessage(contract.contractMessage);
|
|
99
|
+
bridge.settle({ kind: 'accepted', signature });
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
// A WALLET error, and the only one this flow classifies as a
|
|
103
|
+
// possible cancellation on the confirm step.
|
|
104
|
+
bridge.settle({ kind: 'failed', error: err });
|
|
105
|
+
}
|
|
106
|
+
finally {
|
|
107
|
+
// Only if this run still holds it: a `reset()` during the prompt
|
|
108
|
+
// may have started another one, and that one's flag is its own.
|
|
109
|
+
if (this._acceptingFor === token)
|
|
110
|
+
this._acceptingFor = null;
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Back out of the agreement. The run ends on `declined` — a legitimate
|
|
116
|
+
* choice, not a failure, and `state.failure` stays null.
|
|
117
|
+
*
|
|
118
|
+
* Ignored once `acceptContract()` has opened the wallet: an agreement in
|
|
119
|
+
* the middle of being signed cannot also be refused. `reset()` is the way
|
|
120
|
+
* out of a prompt that never answers.
|
|
121
|
+
*/
|
|
122
|
+
declineContract() {
|
|
123
|
+
const bridge = this._accept;
|
|
124
|
+
if (!bridge || this._isAccepting())
|
|
125
|
+
return;
|
|
126
|
+
this._accept = null;
|
|
127
|
+
bridge.settle({ kind: 'declined' });
|
|
128
|
+
}
|
|
129
|
+
/** True only while THIS generation is waiting on the acceptance signature. */
|
|
130
|
+
_isAccepting() {
|
|
131
|
+
return (this._acceptingFor !== null &&
|
|
132
|
+
this._store.isCurrent(this._acceptingFor));
|
|
133
|
+
}
|
|
134
|
+
/** `reset()`: unpark the abandoned run and drop its handshake. */
|
|
135
|
+
_onAbandon() {
|
|
136
|
+
const bridge = this._accept;
|
|
137
|
+
this._accept = null;
|
|
138
|
+
this._acceptingFor = null;
|
|
139
|
+
bridge === null || bridge === void 0 ? void 0 : bridge.settle({ kind: 'abandoned' });
|
|
140
|
+
}
|
|
141
|
+
// -----------------------------------------------------------------------
|
|
142
|
+
_run(input, token) {
|
|
143
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
144
|
+
var _a;
|
|
145
|
+
this._accept = null;
|
|
146
|
+
this._acceptingFor = null;
|
|
147
|
+
const ports = this._ports;
|
|
148
|
+
const owner = input.userAddress.toLowerCase();
|
|
149
|
+
// 0. The spender, from the input or from the chain config the facade
|
|
150
|
+
// built this flow with. Without one there is nothing to read an
|
|
151
|
+
// allowance against and nothing to approve — and guessing would
|
|
152
|
+
// grant an approval to the wrong contract.
|
|
153
|
+
const spender = (_a = input.spender) !== null && _a !== void 0 ? _a : this._defaultSpender;
|
|
154
|
+
if (!spender) {
|
|
155
|
+
this._fail(token, true, {
|
|
156
|
+
step: 'generate',
|
|
157
|
+
reason: 'failed',
|
|
158
|
+
error: new Error(NO_SPENDER_MESSAGE),
|
|
159
|
+
});
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
// 1. Allowance pre-check. Decides `approvalRequired` — and therefore
|
|
163
|
+
// the badge total — before the lender is shown a single step. Read
|
|
164
|
+
// live, never cached: an exact-amount approval is fully consumed by
|
|
165
|
+
// the deposit it paid for, so a stale allowance is exactly the value
|
|
166
|
+
// that would wrongly skip the approve and revert the deposit.
|
|
167
|
+
let approvalRequired = true;
|
|
168
|
+
try {
|
|
169
|
+
const allowance = yield ports.readAllowance(owner, spender);
|
|
170
|
+
approvalRequired = allowance.lt(input.amount);
|
|
171
|
+
}
|
|
172
|
+
catch (_b) {
|
|
173
|
+
// A read failure is not a reason to skip an approval. Assume one is
|
|
174
|
+
// needed: the cost is a redundant approve, the alternative is a
|
|
175
|
+
// reverted deposit.
|
|
176
|
+
approvalRequired = true;
|
|
177
|
+
}
|
|
178
|
+
if (!this._store.isCurrent(token))
|
|
179
|
+
return;
|
|
180
|
+
// 2. Generate — sign the auth message.
|
|
181
|
+
this._store.patch(Object.assign({ approvalRequired, phase: 'generating-sign' }, badgeFor('generate', approvalRequired)), token);
|
|
182
|
+
const timestamp = this._now();
|
|
183
|
+
const signedMessage = buildAuthMessage(input, owner, timestamp);
|
|
184
|
+
let signature;
|
|
185
|
+
try {
|
|
186
|
+
signature = yield ports.signMessage(signedMessage);
|
|
187
|
+
}
|
|
188
|
+
catch (err) {
|
|
189
|
+
this._fail(token, approvalRequired, classifyWalletFailure('generate', err));
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
if (!this._store.isCurrent(token))
|
|
193
|
+
return;
|
|
194
|
+
// 3. Generate — POST the request.
|
|
195
|
+
this._store.patch(Object.assign({ phase: 'generating-fetch' }, badgeFor('generate', approvalRequired)), token);
|
|
196
|
+
let contract;
|
|
197
|
+
try {
|
|
198
|
+
contract = yield ports.generateContract(Object.assign({ address: owner, signature,
|
|
199
|
+
timestamp,
|
|
200
|
+
signedMessage, poolId: input.poolId, trancheId: input.trancheId, fixedTermConfigId: input.fixedTermConfigId, depositAmount: input.depositAmount }, displayFieldsOf(input.contractMessage)));
|
|
201
|
+
}
|
|
202
|
+
catch (err) {
|
|
203
|
+
// Never a wallet rejection: this step is an HTTP call, and the
|
|
204
|
+
// lender's wallet was not involved in it. A backend that happened
|
|
205
|
+
// to echo the words "user rejected" must not be reported to them as
|
|
206
|
+
// something they did.
|
|
207
|
+
this._fail(token, approvalRequired, {
|
|
208
|
+
step: 'generate',
|
|
209
|
+
reason: 'failed',
|
|
210
|
+
error: err,
|
|
211
|
+
});
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
if (!this._store.isCurrent(token))
|
|
215
|
+
return;
|
|
216
|
+
// 4. Park on the agreement until the consumer accepts or declines.
|
|
217
|
+
const outcome = yield new Promise((resolve) => {
|
|
218
|
+
this._accept = { settle: resolve };
|
|
219
|
+
this._store.patch(Object.assign({ phase: 'awaiting-accept', contract }, badgeFor('confirm', approvalRequired)), token);
|
|
220
|
+
});
|
|
221
|
+
this._accept = null;
|
|
222
|
+
// An abandoned run lands here too — `reset()` unparks it. The token
|
|
223
|
+
// check is what tells the two apart.
|
|
224
|
+
if (!this._store.isCurrent(token))
|
|
225
|
+
return;
|
|
226
|
+
if (outcome.kind === 'declined') {
|
|
227
|
+
this._store.patch(Object.assign({ phase: 'declined' }, badgeFor('confirm', approvalRequired)), token);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
if (outcome.kind !== 'accepted') {
|
|
231
|
+
if (outcome.kind === 'failed') {
|
|
232
|
+
this._fail(token, approvalRequired, classifyWalletFailure('confirm', outcome.error));
|
|
233
|
+
}
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
// 5. TTL guard. Checked here because this is where the idling happens:
|
|
237
|
+
// the lender has just spent as long as they wanted reading. An
|
|
238
|
+
// expired agreement is refused rather than broadcast — the on-chain
|
|
239
|
+
// call would revert, after a wallet prompt and a gas estimate, with
|
|
240
|
+
// nothing on screen explaining why.
|
|
241
|
+
if (this._now() > contract.timestamp + this._ttlMs) {
|
|
242
|
+
this._fail(token, approvalRequired, {
|
|
243
|
+
step: 'request',
|
|
244
|
+
reason: 'contract-expired',
|
|
245
|
+
});
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
const depositData = encodeDepositData({
|
|
249
|
+
signature: outcome.signature,
|
|
250
|
+
timestamp: contract.timestamp,
|
|
251
|
+
contractVersion: contract.contractVersion,
|
|
252
|
+
contractType: asContractType(contract.contractType),
|
|
253
|
+
});
|
|
254
|
+
// 6. Approve — the EXACT amount, never `MaxUint256`. House rule: an
|
|
255
|
+
// unlimited allowance outlives the deposit it was granted for, and a
|
|
256
|
+
// later exploit of the spender would drain a wallet that has long
|
|
257
|
+
// since stopped lending.
|
|
258
|
+
if (approvalRequired) {
|
|
259
|
+
this._store.patch(Object.assign({ phase: 'approve' }, badgeFor('approve', approvalRequired)), token);
|
|
260
|
+
try {
|
|
261
|
+
const tx = yield ports.approve(spender, input.amount);
|
|
262
|
+
yield tx.wait();
|
|
263
|
+
}
|
|
264
|
+
catch (err) {
|
|
265
|
+
this._fail(token, approvalRequired, classifyWalletFailure('approve', err));
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
if (!this._store.isCurrent(token))
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
// 7. Request — KYC signature, then the deposit and its receipt.
|
|
272
|
+
this._store.patch(Object.assign({ phase: 'request-sign' }, badgeFor('request', approvalRequired)), token);
|
|
273
|
+
// The two KYC ports reach the consumer's own backend, so they fail the
|
|
274
|
+
// way the generate step does: `failed`, with the error kept. Running
|
|
275
|
+
// them through the rejection classifier would let a backend wording —
|
|
276
|
+
// "Declined", "request rejected" — end a run as "you cancelled in your
|
|
277
|
+
// wallet", with the real error discarded and nothing to report.
|
|
278
|
+
let kyc;
|
|
279
|
+
try {
|
|
280
|
+
const kycParams = yield ports.buildKycParams(owner);
|
|
281
|
+
kyc = yield ports.getKycSignature(kycParams);
|
|
282
|
+
}
|
|
283
|
+
catch (err) {
|
|
284
|
+
this._fail(token, approvalRequired, {
|
|
285
|
+
step: 'request',
|
|
286
|
+
reason: 'failed',
|
|
287
|
+
error: err,
|
|
288
|
+
});
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
if (!this._store.isCurrent(token))
|
|
292
|
+
return;
|
|
293
|
+
try {
|
|
294
|
+
const tx = yield ports.deposit({
|
|
295
|
+
poolId: input.poolId,
|
|
296
|
+
trancheId: input.trancheId,
|
|
297
|
+
amount: input.amount,
|
|
298
|
+
kycSignature: {
|
|
299
|
+
blockExpiration: kyc.blockExpiration,
|
|
300
|
+
signature: kyc.signature,
|
|
301
|
+
},
|
|
302
|
+
depositData,
|
|
303
|
+
fixedTermConfigId: input.fixedTermConfigId,
|
|
304
|
+
});
|
|
305
|
+
this._store.patch(Object.assign({ phase: 'request-confirm' }, badgeFor('request', approvalRequired)), token);
|
|
306
|
+
yield tx.wait();
|
|
307
|
+
}
|
|
308
|
+
catch (err) {
|
|
309
|
+
this._fail(token, approvalRequired, classifyRequest(err));
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
if (!this._store.isCurrent(token))
|
|
313
|
+
return;
|
|
314
|
+
this._store.patch(Object.assign({ phase: 'success' }, badgeFor('request', approvalRequired)), token);
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
_fail(token, approvalRequired, failure) {
|
|
318
|
+
this._store.patch(Object.assign({ phase: 'error', failure }, badgeFor(failure.step, approvalRequired)), token);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
// ---------------------------------------------------------------------------
|
|
322
|
+
// Message building
|
|
323
|
+
// ---------------------------------------------------------------------------
|
|
324
|
+
/**
|
|
325
|
+
* The auth message, in whichever format the consumer asked for. Both builders
|
|
326
|
+
* are the byte-exact protocol strings from `domain/loan-contract` — the string
|
|
327
|
+
* signed here and the body posted from `_run` state the SAME timestamp and the
|
|
328
|
+
* SAME lowercased address, because kasu-backend rebuilds one from the other.
|
|
329
|
+
*/
|
|
330
|
+
function buildAuthMessage(input, owner, timestamp) {
|
|
331
|
+
if (input.contractMessage.format === 'legacy') {
|
|
332
|
+
return buildLegacyContractRequestMessage(owner, timestamp);
|
|
333
|
+
}
|
|
334
|
+
const { strategyName, region, optionName, amountLabel } = input.contractMessage;
|
|
335
|
+
return buildLoanAgreementSignMessage({
|
|
336
|
+
strategyName,
|
|
337
|
+
region,
|
|
338
|
+
optionName,
|
|
339
|
+
amountLabel,
|
|
340
|
+
timestamp,
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* The four display fields, present only on the new format. The backend picks
|
|
345
|
+
* its verification path on their presence: all four → the human-readable
|
|
346
|
+
* format, any missing → the legacy string.
|
|
347
|
+
*/
|
|
348
|
+
function displayFieldsOf(message) {
|
|
349
|
+
if (message.format === 'legacy')
|
|
350
|
+
return {};
|
|
351
|
+
return {
|
|
352
|
+
strategyName: message.strategyName,
|
|
353
|
+
region: message.region,
|
|
354
|
+
optionName: message.optionName,
|
|
355
|
+
amountLabel: message.amountLabel,
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
//# sourceMappingURL=deposit-flow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deposit-flow.js","sourceRoot":"","sources":["../../src/flows/deposit-flow.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { FlowStore } from './observable';
|
|
2
|
+
/**
|
|
3
|
+
* The run lifecycle both flows share: one run at a time, every transition
|
|
4
|
+
* published, and a `reset()` that is safe to call in the middle of one.
|
|
5
|
+
*
|
|
6
|
+
* `DepositFlow` and `WithdrawFlow` each carried their own copy of this — the
|
|
7
|
+
* re-entrancy guard, the `try/finally` around the run, `state`, `isRunning`,
|
|
8
|
+
* `subscribe`, `reset`. Two copies of a guard is two places for a guard to be
|
|
9
|
+
* wrong, on the money path, which is the same reason the pipelines themselves
|
|
10
|
+
* moved out of the applications.
|
|
11
|
+
*
|
|
12
|
+
* ## The guard and the run token are separate, and both matter
|
|
13
|
+
*
|
|
14
|
+
* `_runToken` is the re-entrancy guard: claimed synchronously, before the
|
|
15
|
+
* first `await`, so a double tap cannot launch two pipelines. The store's
|
|
16
|
+
* `generation` is the abandonment mechanism: an in-flight run holds the value
|
|
17
|
+
* it claimed, and every `patch` and `isCurrent` checkpoint it makes after
|
|
18
|
+
* `reset()` is dropped.
|
|
19
|
+
*
|
|
20
|
+
* `reset()` releases the guard IMMEDIATELY rather than waiting for the
|
|
21
|
+
* abandoned run to notice. It has to: a run parked on a wallet prompt that
|
|
22
|
+
* never answers is a promise that may never settle, and a consumer that has
|
|
23
|
+
* left that screen must be able to start a new run in the same tick. The
|
|
24
|
+
* abandoned run's `finally` therefore releases the guard only if it still
|
|
25
|
+
* holds it — by then a NEW run may own it, and clearing it there would let a
|
|
26
|
+
* third run start alongside the second.
|
|
27
|
+
*/
|
|
28
|
+
export declare abstract class Flow<S, I> {
|
|
29
|
+
protected readonly _store: FlowStore<S>;
|
|
30
|
+
/**
|
|
31
|
+
* The token of the run currently holding the guard, or `null` when none
|
|
32
|
+
* does. Not a boolean: releasing the guard has to be attributable to the
|
|
33
|
+
* run that claimed it.
|
|
34
|
+
*/
|
|
35
|
+
private _runToken;
|
|
36
|
+
protected constructor(initial: S);
|
|
37
|
+
/** The current state. Every transition is also published to `subscribe`. */
|
|
38
|
+
get state(): S;
|
|
39
|
+
/** True while a run is in flight, including while parked. */
|
|
40
|
+
get isRunning(): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Observe every transition. Returns the unsubscribe function.
|
|
43
|
+
*
|
|
44
|
+
* The listener is not called on subscribe; read `state` for the value it
|
|
45
|
+
* starts from.
|
|
46
|
+
*/
|
|
47
|
+
subscribe(listener: (state: S) => void): () => void;
|
|
48
|
+
/**
|
|
49
|
+
* Run the pipeline. Resolves when it reaches a terminal phase — it does
|
|
50
|
+
* not reject, because every outcome a consumer can act on is in `state`.
|
|
51
|
+
*
|
|
52
|
+
* A second call while one is in flight is a no-op: one submission per
|
|
53
|
+
* intent, however many times the button is pressed.
|
|
54
|
+
*/
|
|
55
|
+
start(input: I): Promise<void>;
|
|
56
|
+
/**
|
|
57
|
+
* Back to `idle`, abandoning any run in flight: its remaining transitions
|
|
58
|
+
* are dropped, whatever it is parked on is unparked, and the guard is
|
|
59
|
+
* released at once so `start()` is accepted in the SAME tick. Subscribers
|
|
60
|
+
* are kept — the consumer watching the last run is the one watching the
|
|
61
|
+
* next.
|
|
62
|
+
*/
|
|
63
|
+
reset(): void;
|
|
64
|
+
/**
|
|
65
|
+
* Hook for a flow that parks: unpark it. Called by `reset()` after the
|
|
66
|
+
* generation has been bumped, so anything the abandoned run does with the
|
|
67
|
+
* result is dropped.
|
|
68
|
+
*/
|
|
69
|
+
protected _onAbandon(): void;
|
|
70
|
+
/** The pipeline itself. `token` is this run's; check it after every await. */
|
|
71
|
+
protected abstract _run(input: I, token: number): Promise<void>;
|
|
72
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { FlowStore } from './observable';
|
|
11
|
+
/**
|
|
12
|
+
* The run lifecycle both flows share: one run at a time, every transition
|
|
13
|
+
* published, and a `reset()` that is safe to call in the middle of one.
|
|
14
|
+
*
|
|
15
|
+
* `DepositFlow` and `WithdrawFlow` each carried their own copy of this — the
|
|
16
|
+
* re-entrancy guard, the `try/finally` around the run, `state`, `isRunning`,
|
|
17
|
+
* `subscribe`, `reset`. Two copies of a guard is two places for a guard to be
|
|
18
|
+
* wrong, on the money path, which is the same reason the pipelines themselves
|
|
19
|
+
* moved out of the applications.
|
|
20
|
+
*
|
|
21
|
+
* ## The guard and the run token are separate, and both matter
|
|
22
|
+
*
|
|
23
|
+
* `_runToken` is the re-entrancy guard: claimed synchronously, before the
|
|
24
|
+
* first `await`, so a double tap cannot launch two pipelines. The store's
|
|
25
|
+
* `generation` is the abandonment mechanism: an in-flight run holds the value
|
|
26
|
+
* it claimed, and every `patch` and `isCurrent` checkpoint it makes after
|
|
27
|
+
* `reset()` is dropped.
|
|
28
|
+
*
|
|
29
|
+
* `reset()` releases the guard IMMEDIATELY rather than waiting for the
|
|
30
|
+
* abandoned run to notice. It has to: a run parked on a wallet prompt that
|
|
31
|
+
* never answers is a promise that may never settle, and a consumer that has
|
|
32
|
+
* left that screen must be able to start a new run in the same tick. The
|
|
33
|
+
* abandoned run's `finally` therefore releases the guard only if it still
|
|
34
|
+
* holds it — by then a NEW run may own it, and clearing it there would let a
|
|
35
|
+
* third run start alongside the second.
|
|
36
|
+
*/
|
|
37
|
+
export class Flow {
|
|
38
|
+
constructor(initial) {
|
|
39
|
+
/**
|
|
40
|
+
* The token of the run currently holding the guard, or `null` when none
|
|
41
|
+
* does. Not a boolean: releasing the guard has to be attributable to the
|
|
42
|
+
* run that claimed it.
|
|
43
|
+
*/
|
|
44
|
+
this._runToken = null;
|
|
45
|
+
this._store = new FlowStore(initial);
|
|
46
|
+
}
|
|
47
|
+
/** The current state. Every transition is also published to `subscribe`. */
|
|
48
|
+
get state() {
|
|
49
|
+
return this._store.state;
|
|
50
|
+
}
|
|
51
|
+
/** True while a run is in flight, including while parked. */
|
|
52
|
+
get isRunning() {
|
|
53
|
+
return this._runToken !== null;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Observe every transition. Returns the unsubscribe function.
|
|
57
|
+
*
|
|
58
|
+
* The listener is not called on subscribe; read `state` for the value it
|
|
59
|
+
* starts from.
|
|
60
|
+
*/
|
|
61
|
+
subscribe(listener) {
|
|
62
|
+
return this._store.subscribe(listener);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Run the pipeline. Resolves when it reaches a terminal phase — it does
|
|
66
|
+
* not reject, because every outcome a consumer can act on is in `state`.
|
|
67
|
+
*
|
|
68
|
+
* A second call while one is in flight is a no-op: one submission per
|
|
69
|
+
* intent, however many times the button is pressed.
|
|
70
|
+
*/
|
|
71
|
+
start(input) {
|
|
72
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
73
|
+
if (this._runToken !== null)
|
|
74
|
+
return;
|
|
75
|
+
this._store.reset();
|
|
76
|
+
const token = this._store.beginRun();
|
|
77
|
+
this._runToken = token;
|
|
78
|
+
try {
|
|
79
|
+
yield this._run(input, token);
|
|
80
|
+
}
|
|
81
|
+
finally {
|
|
82
|
+
if (this._runToken === token)
|
|
83
|
+
this._runToken = null;
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Back to `idle`, abandoning any run in flight: its remaining transitions
|
|
89
|
+
* are dropped, whatever it is parked on is unparked, and the guard is
|
|
90
|
+
* released at once so `start()` is accepted in the SAME tick. Subscribers
|
|
91
|
+
* are kept — the consumer watching the last run is the one watching the
|
|
92
|
+
* next.
|
|
93
|
+
*/
|
|
94
|
+
reset() {
|
|
95
|
+
this._runToken = null;
|
|
96
|
+
// Bump the generation BEFORE unparking, so the abandoned run's next
|
|
97
|
+
// checkpoint already reads as superseded.
|
|
98
|
+
this._store.reset();
|
|
99
|
+
this._onAbandon();
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Hook for a flow that parks: unpark it. Called by `reset()` after the
|
|
103
|
+
* generation has been bumped, so anything the abandoned run does with the
|
|
104
|
+
* result is dropped.
|
|
105
|
+
*/
|
|
106
|
+
_onAbandon() {
|
|
107
|
+
// A flow with nothing parked has nothing to unpark.
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=flow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flow.js","sourceRoot":"","sources":["../../src/flows/flow.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,OAAgB,IAAI;IAUtB,YAAsB,OAAU;QAPhC;;;;WAIG;QACK,cAAS,GAAkB,IAAI,CAAC;QAGpC,IAAI,CAAC,MAAM,GAAG,IAAI,SAAS,CAAI,OAAO,CAAC,CAAC;IAC5C,CAAC;IAED,4EAA4E;IAC5E,IAAI,KAAK;QACL,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;IAC7B,CAAC;IAED,6DAA6D;IAC7D,IAAI,SAAS;QACT,OAAO,IAAI,CAAC,SAAS,KAAK,IAAI,CAAC;IACnC,CAAC;IAED;;;;;OAKG;IACH,SAAS,CAAC,QAA4B;QAClC,OAAO,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IAC3C,CAAC;IAED;;;;;;OAMG;IACG,KAAK,CAAC,KAAQ;;YAChB,IAAI,IAAI,CAAC,SAAS,KAAK,IAAI;gBAAE,OAAO;YACpC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YACpB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;YACrC,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;YACvB,IAAI,CAAC;gBACD,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;YAClC,CAAC;oBAAS,CAAC;gBACP,IAAI,IAAI,CAAC,SAAS,KAAK,KAAK;oBAAE,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;YACxD,CAAC;QACL,CAAC;KAAA;IAED;;;;;;OAMG;IACH,KAAK;QACD,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,oEAAoE;QACpE,0CAA0C;QAC1C,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QACpB,IAAI,CAAC,UAAU,EAAE,CAAC;IACtB,CAAC;IAED;;;;OAIG;IACO,UAAU;QAChB,oDAAoD;IACxD,CAAC;CAIJ"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flows: the headless money-path state machines.
|
|
3
|
+
*
|
|
4
|
+
* `domain/` holds rules that are pure functions. These are the same idea one
|
|
5
|
+
* level up — a rule that unfolds over time, with side effects the consumer
|
|
6
|
+
* injects as ports. They own the ORDER of a deposit and a withdrawal, the
|
|
7
|
+
* guards around them, and the codes that describe where a run got to. They own
|
|
8
|
+
* no UI, no framework, no network and no words.
|
|
9
|
+
*
|
|
10
|
+
* Every application drove its own copy of the deposit pipeline before this, and
|
|
11
|
+
* the copies had already started to differ.
|
|
12
|
+
*/
|
|
13
|
+
export { Flow } from './flow';
|
|
14
|
+
export { FlowStore } from './observable';
|
|
15
|
+
export type { WaitableTransaction } from './observable';
|
|
16
|
+
export { CONTRACT_TTL_MS, DepositFlow, NO_SPENDER_MESSAGE, } from './deposit-flow';
|
|
17
|
+
export type { ContractMessageRequest, DepositFailure, DepositFlowInput, DepositFlowOptions, DepositPhase, DepositPorts, DepositState, DepositStep, GenerateContractRequest, KycSignature, LegacyContractRequest, LoanAgreementRequest, } from './deposit-flow';
|
|
18
|
+
export { NO_KYC_PARAMS_MESSAGE, WithdrawFlow } from './withdraw-flow';
|
|
19
|
+
export type { WithdrawFailure, WithdrawFlowInput, WithdrawPhase, WithdrawPorts, WithdrawState, WithdrawStep, } from './withdraw-flow';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flows: the headless money-path state machines.
|
|
3
|
+
*
|
|
4
|
+
* `domain/` holds rules that are pure functions. These are the same idea one
|
|
5
|
+
* level up — a rule that unfolds over time, with side effects the consumer
|
|
6
|
+
* injects as ports. They own the ORDER of a deposit and a withdrawal, the
|
|
7
|
+
* guards around them, and the codes that describe where a run got to. They own
|
|
8
|
+
* no UI, no framework, no network and no words.
|
|
9
|
+
*
|
|
10
|
+
* Every application drove its own copy of the deposit pipeline before this, and
|
|
11
|
+
* the copies had already started to differ.
|
|
12
|
+
*/
|
|
13
|
+
export { Flow } from './flow';
|
|
14
|
+
export { FlowStore } from './observable';
|
|
15
|
+
export { CONTRACT_TTL_MS, DepositFlow, NO_SPENDER_MESSAGE, } from './deposit-flow';
|
|
16
|
+
export { NO_KYC_PARAMS_MESSAGE, WithdrawFlow } from './withdraw-flow';
|
|
17
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/flows/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGzC,OAAO,EACH,eAAe,EACf,WAAW,EACX,kBAAkB,GACrB,MAAM,gBAAgB,CAAC;AAgBxB,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The tiny observable-state kernel both flows are built on.
|
|
3
|
+
*
|
|
4
|
+
* Deliberately not a framework store: no React, no signals, no dependency.
|
|
5
|
+
* A flow is a long asynchronous run that has to publish every transition to
|
|
6
|
+
* whatever is rendering it, and has to be able to abandon a run that the
|
|
7
|
+
* consumer walked away from. That is the whole requirement, and it is 60 lines.
|
|
8
|
+
*
|
|
9
|
+
* The `generation` counter is the abandonment mechanism. `reset()` bumps it;
|
|
10
|
+
* an in-flight run captured the previous value, so every later `patch` it
|
|
11
|
+
* attempts is dropped and every `isCurrent` checkpoint tells it to stop. It is
|
|
12
|
+
* what makes `reset()` safe to call mid-flight — the run cannot resurrect a
|
|
13
|
+
* dead state or, worse, drive the UI back into a phase the consumer has left.
|
|
14
|
+
*/
|
|
15
|
+
export declare class FlowStore<S> {
|
|
16
|
+
private readonly _initial;
|
|
17
|
+
private _state;
|
|
18
|
+
private _generation;
|
|
19
|
+
private readonly _listeners;
|
|
20
|
+
constructor(_initial: S);
|
|
21
|
+
get state(): S;
|
|
22
|
+
/** The run token an in-flight pipeline captures at `start()`. */
|
|
23
|
+
get generation(): number;
|
|
24
|
+
/** False once `reset()` (or a later `start()`) has superseded `token`. */
|
|
25
|
+
isCurrent(token: number): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Subscribe to every transition. Returns the unsubscribe function; calling
|
|
28
|
+
* it twice is harmless.
|
|
29
|
+
*
|
|
30
|
+
* Listeners are NOT called on subscribe — read `state` for the current
|
|
31
|
+
* value. A listener that throws is not allowed to break the pipeline or
|
|
32
|
+
* starve the listeners registered after it, so throws are swallowed.
|
|
33
|
+
*/
|
|
34
|
+
subscribe(listener: (state: S) => void): () => void;
|
|
35
|
+
/**
|
|
36
|
+
* Merge `next` into the state and notify, unless `token` names a
|
|
37
|
+
* superseded run.
|
|
38
|
+
*/
|
|
39
|
+
patch(next: Partial<S>, token?: number): void;
|
|
40
|
+
/**
|
|
41
|
+
* Back to the initial state, abandoning any in-flight run.
|
|
42
|
+
*
|
|
43
|
+
* Subscribers are kept: the consumer that was watching the last run is the
|
|
44
|
+
* one that will watch the next.
|
|
45
|
+
*/
|
|
46
|
+
reset(): void;
|
|
47
|
+
/** Claim the next run token. Any earlier run is abandoned. */
|
|
48
|
+
beginRun(): number;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Anything with a `wait()` — an ethers `ContractTransaction`, or a fake.
|
|
52
|
+
*
|
|
53
|
+
* Lives beside the store rather than in either flow: both submit transactions
|
|
54
|
+
* through their own ports, and neither of them should have to import the
|
|
55
|
+
* other's file to say so.
|
|
56
|
+
*/
|
|
57
|
+
export interface WaitableTransaction {
|
|
58
|
+
wait(): Promise<unknown>;
|
|
59
|
+
}
|