@kasufinance/kasu-sdk 2.5.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 +144 -7
- package/dist/bundle.cjs.js +1667 -21
- package/dist/bundle.esm.js +1630 -22
- package/dist/domain/au-minimum.d.ts +135 -0
- package/dist/domain/au-minimum.js +154 -0
- package/dist/domain/au-minimum.js.map +1 -0
- package/dist/domain/index.d.ts +18 -3
- package/dist/domain/index.js +13 -3
- package/dist/domain/index.js.map +1 -1
- package/dist/domain/loan-contract.d.ts +174 -0
- package/dist/domain/loan-contract.js +160 -0
- package/dist/domain/loan-contract.js.map +1 -0
- package/dist/domain/requests.d.ts +219 -0
- package/dist/domain/requests.js +218 -0
- package/dist/domain/requests.js.map +1 -0
- package/dist/domain/settlement.d.ts +97 -0
- package/dist/domain/settlement.js +117 -0
- package/dist/domain/settlement.js.map +1 -0
- package/dist/domain/wallet-errors.d.ts +92 -0
- package/dist/domain/wallet-errors.js +155 -0
- package/dist/domain/wallet-errors.js.map +1 -0
- package/dist/facade/chain-configs.js +7 -1
- package/dist/facade/chain-configs.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/facade/user-portfolio.d.ts +18 -0
- package/dist/facade/user-portfolio.js +23 -0
- package/dist/facade/user-portfolio.js.map +1 -1
- 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 +2 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/services/DataService/data-service.js +3 -11
- package/dist/services/DataService/data-service.js.map +1 -1
- package/dist/services/DataService/directus-client.d.ts +26 -0
- package/dist/services/DataService/directus-client.js +38 -0
- package/dist/services/DataService/directus-client.js.map +1 -0
- package/dist/services/UserLending/user-lending.js +11 -7
- package/dist/services/UserLending/user-lending.js.map +1 -1
- package/package.json +8 -2
- package/src/domain/au-minimum.ts +192 -0
- package/src/domain/index.ts +73 -3
- package/src/domain/loan-contract.ts +275 -0
- package/src/domain/requests.ts +467 -0
- package/src/domain/settlement.ts +161 -0
- package/src/domain/wallet-errors.ts +173 -0
- package/src/facade/chain-configs.ts +7 -1
- 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/facade/user-portfolio.ts +24 -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 +9 -0
- package/src/services/DataService/data-service.ts +7 -25
- package/src/services/DataService/directus-client.ts +54 -0
- package/src/services/UserLending/user-lending.ts +17 -21
- 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/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/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/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 -201
- 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/deposit-bounds.test.ts +0 -200
- 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/tranche-display-name.test.ts +0 -96
- package/src/domain/tranches.test.ts +0 -300
- package/src/facade/config.test.ts +0 -265
- package/src/facade/facade.test.ts +0 -250
- package/src/services/Locking/calculate-apy.test.ts +0 -52
- package/src/tests/sample.test.ts +0 -63
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The AU cumulative-lending minimum — the numeric half.
|
|
3
|
+
*
|
|
4
|
+
* RULE: a lender whose verified KYC country is Australia may only lend when
|
|
5
|
+
* their existing deposited position on THIS deployment, plus the amount they
|
|
6
|
+
* are asking for, reaches the deployment's threshold.
|
|
7
|
+
*
|
|
8
|
+
* This is UX PRE-VALIDATION ONLY. kasu-backend enforces the rule
|
|
9
|
+
* authoritatively at contract generation and refuses with HTTP 403
|
|
10
|
+
* `AU_WHOLESALE_MINIMUM_NOT_MET`; a lender who gets past this check is still
|
|
11
|
+
* stopped there. What these helpers exist for is to raise the amount field's
|
|
12
|
+
* minimum so the lender learns the rule while typing rather than at the end.
|
|
13
|
+
*
|
|
14
|
+
* FAIL-OPEN by design: an absent or unknown country is NOT restricted, and an
|
|
15
|
+
* unlisted stable symbol has no threshold. An UNKNOWN existing position is
|
|
16
|
+
* treated as 0 — strict, so the floor is never advertised lower than the
|
|
17
|
+
* backend will accept, and it relaxes once the position loads.
|
|
18
|
+
*
|
|
19
|
+
* Arithmetic is integer bigint in minor units (10^decimals) so no two
|
|
20
|
+
* consumers can disagree at the boundary. Parsing TRUNCATES, never rounds up —
|
|
21
|
+
* a position can never be inflated into passing. `BigInt(...)` calls only, no
|
|
22
|
+
* bigint literals, so consumers on an older target still compile.
|
|
23
|
+
*
|
|
24
|
+
* Lifted from kasu-ui's `features/lending/lib/au-lending-restriction.ts`. The
|
|
25
|
+
* two toast strings that live there are copy and stay in the applications.
|
|
26
|
+
*/
|
|
27
|
+
type Maybe<T> = T | null | undefined;
|
|
28
|
+
type NumLike = string | number | null | undefined;
|
|
29
|
+
/** ISO 3166-1 alpha-3 for Australia — the collapsed KYC country field. */
|
|
30
|
+
export declare const AU_ALPHA3 = "AUS";
|
|
31
|
+
/**
|
|
32
|
+
* Is this the Australian KYC country? Case-insensitive and
|
|
33
|
+
* whitespace-tolerant. Anything that is not a string — including the
|
|
34
|
+
* `undefined` of an unloaded KYC record — is not restricted (fail-open).
|
|
35
|
+
*
|
|
36
|
+
* The alpha-2 `'AU'` deliberately does NOT match: the KYC country reaching
|
|
37
|
+
* this rule is normalised to alpha-3 upstream, and a bare two-letter code
|
|
38
|
+
* here means something else went wrong.
|
|
39
|
+
*/
|
|
40
|
+
export declare function isAustralianKyc(country: Maybe<string>): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Minimum CUMULATIVE position, in WHOLE stable units, keyed on the
|
|
43
|
+
* deployment's stable-asset symbol (a property of the currency, not the
|
|
44
|
+
* chain). Must match kasu-backend's table exactly.
|
|
45
|
+
*/
|
|
46
|
+
export declare const AU_MIN_CUMULATIVE_BY_STABLE: Readonly<Record<string, number>>;
|
|
47
|
+
/**
|
|
48
|
+
* Threshold in whole units, or `undefined` for an unlisted stable — a
|
|
49
|
+
* deployment whose currency has no configured minimum is unrestricted.
|
|
50
|
+
*/
|
|
51
|
+
export declare function auThresholdFor(symbol: Maybe<string>): number | undefined;
|
|
52
|
+
/**
|
|
53
|
+
* Is this wallet released from the minimum entirely, whatever its country,
|
|
54
|
+
* stable or position says?
|
|
55
|
+
*
|
|
56
|
+
* `exemptAddresses` is DEPLOYMENT CONFIGURATION the caller supplies, not a
|
|
57
|
+
* constant of this package. kasu-sdk is published publicly; the exempt list is
|
|
58
|
+
* a compliance carve-out naming particular lender wallets, so it does not
|
|
59
|
+
* belong in a public tarball. Read it from wherever the application keeps its
|
|
60
|
+
* deployment configuration, and keep it identical to kasu-backend's
|
|
61
|
+
* `AU_WHOLESALE_EXEMPT_ADDRESSES` — that is where the rule is actually
|
|
62
|
+
* enforced. An address exempt here but not there sees no raised minimum in the
|
|
63
|
+
* form and is refused at the end, which is worse than not exempting it at all.
|
|
64
|
+
*
|
|
65
|
+
* The address matched must be the CONNECTED wallet — the one that owns the KYC
|
|
66
|
+
* record and signs the agreement request. kasu-backend verifies that signature
|
|
67
|
+
* before the gate, so an exemption cannot be claimed by asserting someone
|
|
68
|
+
* else's address; it takes their private key. Never match a "view as" address.
|
|
69
|
+
*
|
|
70
|
+
* Comparison is case-insensitive and whitespace-tolerant. Anything that is not
|
|
71
|
+
* a string is NOT exempt (fail closed), and so is anything when the list is
|
|
72
|
+
* absent.
|
|
73
|
+
*/
|
|
74
|
+
export declare function isAuMinimumExempt(address: Maybe<string>, exemptAddresses: Maybe<readonly string[]>): boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Truncating, partial-input-tolerant decimal → minor-unit parse.
|
|
77
|
+
* `'10.'` → `10000000n` · `'1.23456789'` → `1234567n` (truncated at 6dp).
|
|
78
|
+
* `''` / `'abc'` / negative / null → `null`.
|
|
79
|
+
*
|
|
80
|
+
* Tolerant of mid-typing states because it runs on an amount field as the
|
|
81
|
+
* lender types, and truncating because rounding up would let a position that
|
|
82
|
+
* is a fraction short read as passing.
|
|
83
|
+
*/
|
|
84
|
+
export declare function parseMinorUnits(value: NumLike, decimals: number): bigint | null;
|
|
85
|
+
export interface AuMinimumInput {
|
|
86
|
+
/** Collapsed KYC country (alpha-3). Missing/undefined ⇒ not restricted. */
|
|
87
|
+
country: Maybe<string>;
|
|
88
|
+
/**
|
|
89
|
+
* The CONNECTED wallet (never a "view as" override).
|
|
90
|
+
*
|
|
91
|
+
* REQUIRED KEY, nullable value. A caller with no wallet passes `undefined`
|
|
92
|
+
* and gets the un-exempted behaviour; what it may not do is silently omit
|
|
93
|
+
* the key. Optional would let a refactor of the call site drop it with no
|
|
94
|
+
* compile error — and because a form's own minimum validation refuses a
|
|
95
|
+
* submit below the floor, an exempt lender would then be stopped HERE and
|
|
96
|
+
* never reach the backend that would have let them through.
|
|
97
|
+
*/
|
|
98
|
+
address: Maybe<string>;
|
|
99
|
+
/**
|
|
100
|
+
* The deployment's exempt wallets — see `isAuMinimumExempt`. REQUIRED KEY
|
|
101
|
+
* for the same reason `address` is: a caller that has no list passes
|
|
102
|
+
* `undefined` deliberately, and no refactor can drop it by accident.
|
|
103
|
+
*/
|
|
104
|
+
exemptAddresses: Maybe<readonly string[]>;
|
|
105
|
+
/** Deployment stable-asset symbol, e.g. `'USDC'` / `'AUDD'`. */
|
|
106
|
+
stableSymbol: Maybe<string>;
|
|
107
|
+
/** Stable-asset decimals (6 on every current deployment). */
|
|
108
|
+
decimals: number;
|
|
109
|
+
/**
|
|
110
|
+
* Existing position (active + pending) on THIS deployment, whole units.
|
|
111
|
+
* Unparseable/undefined ⇒ treated as 0 (strict: the full threshold applies
|
|
112
|
+
* until the position is known).
|
|
113
|
+
*/
|
|
114
|
+
existingDeposited: NumLike;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* The amount, in WHOLE stable units, an Australian lender still needs in order
|
|
118
|
+
* to reach the deployment's cumulative minimum. Apply it as the amount field's
|
|
119
|
+
* floor via `max(trancheMin, auMinimumRemaining(...))`.
|
|
120
|
+
*
|
|
121
|
+
* Returns 0 for everyone the rule does not restrict (fail-open), 0 for an
|
|
122
|
+
* exempt address, and 0 once the lender's existing position already satisfies
|
|
123
|
+
* the threshold.
|
|
124
|
+
*
|
|
125
|
+
* The exemption is tested FIRST and unconditionally: an exempt wallet has no
|
|
126
|
+
* minimum whatever its country, stable or position says. kasu-backend
|
|
127
|
+
* deliberately tests country first and the exemption second — the two orders
|
|
128
|
+
* are not in conflict, both return "no minimum" for the same inputs. There,
|
|
129
|
+
* the exemption sets a flag that drives a compliance log, so it must not fire
|
|
130
|
+
* for a lender the rule never engaged for. Here nothing is logged, so
|
|
131
|
+
* exemption-first is preferred: it makes this function right on its own,
|
|
132
|
+
* whatever country a caller happens to pass.
|
|
133
|
+
*/
|
|
134
|
+
export declare function auMinimumRemaining(input: AuMinimumInput): number;
|
|
135
|
+
export {};
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The AU cumulative-lending minimum — the numeric half.
|
|
3
|
+
*
|
|
4
|
+
* RULE: a lender whose verified KYC country is Australia may only lend when
|
|
5
|
+
* their existing deposited position on THIS deployment, plus the amount they
|
|
6
|
+
* are asking for, reaches the deployment's threshold.
|
|
7
|
+
*
|
|
8
|
+
* This is UX PRE-VALIDATION ONLY. kasu-backend enforces the rule
|
|
9
|
+
* authoritatively at contract generation and refuses with HTTP 403
|
|
10
|
+
* `AU_WHOLESALE_MINIMUM_NOT_MET`; a lender who gets past this check is still
|
|
11
|
+
* stopped there. What these helpers exist for is to raise the amount field's
|
|
12
|
+
* minimum so the lender learns the rule while typing rather than at the end.
|
|
13
|
+
*
|
|
14
|
+
* FAIL-OPEN by design: an absent or unknown country is NOT restricted, and an
|
|
15
|
+
* unlisted stable symbol has no threshold. An UNKNOWN existing position is
|
|
16
|
+
* treated as 0 — strict, so the floor is never advertised lower than the
|
|
17
|
+
* backend will accept, and it relaxes once the position loads.
|
|
18
|
+
*
|
|
19
|
+
* Arithmetic is integer bigint in minor units (10^decimals) so no two
|
|
20
|
+
* consumers can disagree at the boundary. Parsing TRUNCATES, never rounds up —
|
|
21
|
+
* a position can never be inflated into passing. `BigInt(...)` calls only, no
|
|
22
|
+
* bigint literals, so consumers on an older target still compile.
|
|
23
|
+
*
|
|
24
|
+
* Lifted from kasu-ui's `features/lending/lib/au-lending-restriction.ts`. The
|
|
25
|
+
* two toast strings that live there are copy and stay in the applications.
|
|
26
|
+
*/
|
|
27
|
+
const ZERO = BigInt(0);
|
|
28
|
+
/** ISO 3166-1 alpha-3 for Australia — the collapsed KYC country field. */
|
|
29
|
+
export const AU_ALPHA3 = 'AUS';
|
|
30
|
+
/**
|
|
31
|
+
* Is this the Australian KYC country? Case-insensitive and
|
|
32
|
+
* whitespace-tolerant. Anything that is not a string — including the
|
|
33
|
+
* `undefined` of an unloaded KYC record — is not restricted (fail-open).
|
|
34
|
+
*
|
|
35
|
+
* The alpha-2 `'AU'` deliberately does NOT match: the KYC country reaching
|
|
36
|
+
* this rule is normalised to alpha-3 upstream, and a bare two-letter code
|
|
37
|
+
* here means something else went wrong.
|
|
38
|
+
*/
|
|
39
|
+
export function isAustralianKyc(country) {
|
|
40
|
+
if (typeof country !== 'string')
|
|
41
|
+
return false;
|
|
42
|
+
return country.trim().toUpperCase() === AU_ALPHA3;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Minimum CUMULATIVE position, in WHOLE stable units, keyed on the
|
|
46
|
+
* deployment's stable-asset symbol (a property of the currency, not the
|
|
47
|
+
* chain). Must match kasu-backend's table exactly.
|
|
48
|
+
*/
|
|
49
|
+
export const AU_MIN_CUMULATIVE_BY_STABLE = {
|
|
50
|
+
USDC: 360000,
|
|
51
|
+
AUDD: 500000,
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Threshold in whole units, or `undefined` for an unlisted stable — a
|
|
55
|
+
* deployment whose currency has no configured minimum is unrestricted.
|
|
56
|
+
*/
|
|
57
|
+
export function auThresholdFor(symbol) {
|
|
58
|
+
if (typeof symbol !== 'string')
|
|
59
|
+
return undefined;
|
|
60
|
+
return AU_MIN_CUMULATIVE_BY_STABLE[symbol.trim().toUpperCase()];
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Is this wallet released from the minimum entirely, whatever its country,
|
|
64
|
+
* stable or position says?
|
|
65
|
+
*
|
|
66
|
+
* `exemptAddresses` is DEPLOYMENT CONFIGURATION the caller supplies, not a
|
|
67
|
+
* constant of this package. kasu-sdk is published publicly; the exempt list is
|
|
68
|
+
* a compliance carve-out naming particular lender wallets, so it does not
|
|
69
|
+
* belong in a public tarball. Read it from wherever the application keeps its
|
|
70
|
+
* deployment configuration, and keep it identical to kasu-backend's
|
|
71
|
+
* `AU_WHOLESALE_EXEMPT_ADDRESSES` — that is where the rule is actually
|
|
72
|
+
* enforced. An address exempt here but not there sees no raised minimum in the
|
|
73
|
+
* form and is refused at the end, which is worse than not exempting it at all.
|
|
74
|
+
*
|
|
75
|
+
* The address matched must be the CONNECTED wallet — the one that owns the KYC
|
|
76
|
+
* record and signs the agreement request. kasu-backend verifies that signature
|
|
77
|
+
* before the gate, so an exemption cannot be claimed by asserting someone
|
|
78
|
+
* else's address; it takes their private key. Never match a "view as" address.
|
|
79
|
+
*
|
|
80
|
+
* Comparison is case-insensitive and whitespace-tolerant. Anything that is not
|
|
81
|
+
* a string is NOT exempt (fail closed), and so is anything when the list is
|
|
82
|
+
* absent.
|
|
83
|
+
*/
|
|
84
|
+
export function isAuMinimumExempt(address, exemptAddresses) {
|
|
85
|
+
if (typeof address !== 'string' || !exemptAddresses)
|
|
86
|
+
return false;
|
|
87
|
+
const needle = address.trim().toLowerCase();
|
|
88
|
+
return exemptAddresses.some((a) => a.trim().toLowerCase() === needle);
|
|
89
|
+
}
|
|
90
|
+
const DECIMAL_RE = /^(\d+)?(?:\.(\d*))?$/;
|
|
91
|
+
/**
|
|
92
|
+
* Truncating, partial-input-tolerant decimal → minor-unit parse.
|
|
93
|
+
* `'10.'` → `10000000n` · `'1.23456789'` → `1234567n` (truncated at 6dp).
|
|
94
|
+
* `''` / `'abc'` / negative / null → `null`.
|
|
95
|
+
*
|
|
96
|
+
* Tolerant of mid-typing states because it runs on an amount field as the
|
|
97
|
+
* lender types, and truncating because rounding up would let a position that
|
|
98
|
+
* is a fraction short read as passing.
|
|
99
|
+
*/
|
|
100
|
+
export function parseMinorUnits(value, decimals) {
|
|
101
|
+
if (value === null || value === undefined)
|
|
102
|
+
return null;
|
|
103
|
+
const raw = typeof value === 'number' ? String(value) : value.trim();
|
|
104
|
+
if (!raw)
|
|
105
|
+
return null;
|
|
106
|
+
const match = DECIMAL_RE.exec(raw);
|
|
107
|
+
if (!match || (!match[1] && !match[2]))
|
|
108
|
+
return null;
|
|
109
|
+
// Both capture groups are optional, so either can be absent at runtime.
|
|
110
|
+
// A fractional group that matched EMPTY ('10.') is not absent, and the
|
|
111
|
+
// destructuring defaults leave it alone — only `undefined` takes them.
|
|
112
|
+
const [, whole = '0', fracDigits = ''] = match;
|
|
113
|
+
const frac = fracDigits.slice(0, decimals).padEnd(decimals, '0');
|
|
114
|
+
return BigInt(whole + frac);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* The amount, in WHOLE stable units, an Australian lender still needs in order
|
|
118
|
+
* to reach the deployment's cumulative minimum. Apply it as the amount field's
|
|
119
|
+
* floor via `max(trancheMin, auMinimumRemaining(...))`.
|
|
120
|
+
*
|
|
121
|
+
* Returns 0 for everyone the rule does not restrict (fail-open), 0 for an
|
|
122
|
+
* exempt address, and 0 once the lender's existing position already satisfies
|
|
123
|
+
* the threshold.
|
|
124
|
+
*
|
|
125
|
+
* The exemption is tested FIRST and unconditionally: an exempt wallet has no
|
|
126
|
+
* minimum whatever its country, stable or position says. kasu-backend
|
|
127
|
+
* deliberately tests country first and the exemption second — the two orders
|
|
128
|
+
* are not in conflict, both return "no minimum" for the same inputs. There,
|
|
129
|
+
* the exemption sets a flag that drives a compliance log, so it must not fire
|
|
130
|
+
* for a lender the rule never engaged for. Here nothing is logged, so
|
|
131
|
+
* exemption-first is preferred: it makes this function right on its own,
|
|
132
|
+
* whatever country a caller happens to pass.
|
|
133
|
+
*/
|
|
134
|
+
export function auMinimumRemaining(input) {
|
|
135
|
+
var _a, _b;
|
|
136
|
+
if (isAuMinimumExempt(input.address, input.exemptAddresses))
|
|
137
|
+
return 0;
|
|
138
|
+
if (!isAustralianKyc(input.country))
|
|
139
|
+
return 0;
|
|
140
|
+
const threshold = auThresholdFor(input.stableSymbol);
|
|
141
|
+
if (threshold === undefined)
|
|
142
|
+
return 0;
|
|
143
|
+
const { existingDeposited, decimals } = input;
|
|
144
|
+
// A table threshold always parses; `?? ZERO` keeps the rule fail-open
|
|
145
|
+
// rather than asserting, so an unparseable one yields no minimum at all.
|
|
146
|
+
const thresholdMinor = (_a = parseMinorUnits(threshold, decimals)) !== null && _a !== void 0 ? _a : ZERO;
|
|
147
|
+
const existingMinor = (_b = parseMinorUnits(existingDeposited, decimals)) !== null && _b !== void 0 ? _b : ZERO;
|
|
148
|
+
const remaining = thresholdMinor - existingMinor;
|
|
149
|
+
if (remaining <= ZERO)
|
|
150
|
+
return 0;
|
|
151
|
+
const base = BigInt('1' + '0'.repeat(decimals));
|
|
152
|
+
return Number(remaining / base) + Number(remaining % base) / Number(base);
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=au-minimum.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"au-minimum.js","sourceRoot":"","sources":["../../src/domain/au-minimum.ts"],"names":[],"mappings":"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"}
|
package/dist/domain/index.d.ts
CHANGED
|
@@ -8,19 +8,34 @@
|
|
|
8
8
|
* user reads — `netEffectiveApy` returns `0.196`, never `'19.60% p.a.'`,
|
|
9
9
|
* and `derivePoolStatus` returns the code `'Full'`, not a sentence. The
|
|
10
10
|
* formatters that turn these into text stay in each app, where the design
|
|
11
|
-
* system and the visitor's locale are.
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* system and the visitor's locale are.
|
|
12
|
+
*
|
|
13
|
+
* Two kinds of string are exempt, and only these two. `getTrancheDisplayName`
|
|
14
|
+
* is here precisely because that rename must NOT drift between apps. And
|
|
15
|
+
* `loan-contract.ts` builds PROTOCOL strings — messages kasu-backend
|
|
16
|
+
* reconstructs byte-for-byte to verify a lender's signature. Those are not
|
|
17
|
+
* copy: nobody may reword them, in any language, without a matching backend
|
|
18
|
+
* change, which is exactly why they belong in one place.
|
|
14
19
|
* 2. **Pure.** No network, no clock, no environment. Anything with I/O belongs
|
|
15
20
|
* in `facade/` (see `fetchUnusedPoolIds`).
|
|
16
21
|
*/
|
|
17
22
|
export { apyToEpochRate, epochRateToApy, EPOCHS_IN_YEAR, netEffectiveApy, } from './rates';
|
|
23
|
+
export { AU_ALPHA3, AU_MIN_CUMULATIVE_BY_STABLE, auMinimumRemaining, auThresholdFor, isAuMinimumExempt, isAustralianKyc, parseMinorUnits, } from './au-minimum';
|
|
24
|
+
export type { AuMinimumInput } from './au-minimum';
|
|
18
25
|
export { ceilToCents, floorToCents, isBelowMinimumCapacity, MAX_LENDING_AMOUNT_FALLBACK, MIN_LENDING_AMOUNT_FALLBACK, parseTrancheBound, resolveBoundShortcuts, resolveDepositBounds, } from './deposit-bounds';
|
|
19
26
|
export type { BoundShortcuts, DepositBounds } from './deposit-bounds';
|
|
20
27
|
export { APXIUM, getCreditOriginator, getInstitutionalLender, INVOICEMATE, RIXON_CAPITAL, } from './partners';
|
|
21
28
|
export type { PoolNameSignal, StrategyPartner } from './partners';
|
|
29
|
+
export { asContractType, buildContractVersionType, buildFullNameRequestMessage, buildLegacyContractRequestMessage, buildLoanAgreementSignMessage, encodeDepositData, formatSignTimestampUtc, parseFormattedMessage, } from './loan-contract';
|
|
30
|
+
export type { ContractListItem, ContractSection, ContractType, ExemptLoanContract, GenerateContractResponse, LoanContractFormatted, ResolvedContractResponse, RetailLoanContract, } from './loan-contract';
|
|
22
31
|
export { maxNetRateCeiling, pickHighestYieldTranche, poolMaxApy, selectVisiblePools, } from './pools';
|
|
23
32
|
export type { BestTranche } from './pools';
|
|
33
|
+
export { countSubmissions, deriveRequestState, firstSubmissionTimestamp, isCycleClosed, lastEventTimestamp, submissionEvents, } from './requests';
|
|
34
|
+
export type { RequestKind, RequestState, RequestStatusCode, } from './requests';
|
|
35
|
+
export { CLEARING_WINDOW_SECONDS, computeSettlementWindow, deriveCycleDates, nextCycleBoundary, } from './settlement';
|
|
36
|
+
export type { CycleDates, SettlementWindowInput, SettlementWindowState, } from './settlement';
|
|
24
37
|
export { getTrancheDisplayName, UPPER_MEZZANINE } from './tranche-display-name';
|
|
25
38
|
export { compareTrancheSeniority, derivePoolStatus, MIN_TRANCHE_CAPACITY, netTrancheApyBounds, pickDefaultTrancheId, poolAllTranchesFull, trancheApyBounds, trancheHasCapacity, trancheRiskRank, } from './tranches';
|
|
26
39
|
export type { ApyBounds, PoolStatus, TrancheCapacitySignal, } from './tranches';
|
|
40
|
+
export { classifyWalletFailure, isUnpredictableGas, isUserRejected, } from './wallet-errors';
|
|
41
|
+
export type { WalletFailure } from './wallet-errors';
|
package/dist/domain/index.js
CHANGED
|
@@ -8,16 +8,26 @@
|
|
|
8
8
|
* user reads — `netEffectiveApy` returns `0.196`, never `'19.60% p.a.'`,
|
|
9
9
|
* and `derivePoolStatus` returns the code `'Full'`, not a sentence. The
|
|
10
10
|
* formatters that turn these into text stay in each app, where the design
|
|
11
|
-
* system and the visitor's locale are.
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* system and the visitor's locale are.
|
|
12
|
+
*
|
|
13
|
+
* Two kinds of string are exempt, and only these two. `getTrancheDisplayName`
|
|
14
|
+
* is here precisely because that rename must NOT drift between apps. And
|
|
15
|
+
* `loan-contract.ts` builds PROTOCOL strings — messages kasu-backend
|
|
16
|
+
* reconstructs byte-for-byte to verify a lender's signature. Those are not
|
|
17
|
+
* copy: nobody may reword them, in any language, without a matching backend
|
|
18
|
+
* change, which is exactly why they belong in one place.
|
|
14
19
|
* 2. **Pure.** No network, no clock, no environment. Anything with I/O belongs
|
|
15
20
|
* in `facade/` (see `fetchUnusedPoolIds`).
|
|
16
21
|
*/
|
|
17
22
|
export { apyToEpochRate, epochRateToApy, EPOCHS_IN_YEAR, netEffectiveApy, } from './rates';
|
|
23
|
+
export { AU_ALPHA3, AU_MIN_CUMULATIVE_BY_STABLE, auMinimumRemaining, auThresholdFor, isAuMinimumExempt, isAustralianKyc, parseMinorUnits, } from './au-minimum';
|
|
18
24
|
export { ceilToCents, floorToCents, isBelowMinimumCapacity, MAX_LENDING_AMOUNT_FALLBACK, MIN_LENDING_AMOUNT_FALLBACK, parseTrancheBound, resolveBoundShortcuts, resolveDepositBounds, } from './deposit-bounds';
|
|
19
25
|
export { APXIUM, getCreditOriginator, getInstitutionalLender, INVOICEMATE, RIXON_CAPITAL, } from './partners';
|
|
26
|
+
export { asContractType, buildContractVersionType, buildFullNameRequestMessage, buildLegacyContractRequestMessage, buildLoanAgreementSignMessage, encodeDepositData, formatSignTimestampUtc, parseFormattedMessage, } from './loan-contract';
|
|
20
27
|
export { maxNetRateCeiling, pickHighestYieldTranche, poolMaxApy, selectVisiblePools, } from './pools';
|
|
28
|
+
export { countSubmissions, deriveRequestState, firstSubmissionTimestamp, isCycleClosed, lastEventTimestamp, submissionEvents, } from './requests';
|
|
29
|
+
export { CLEARING_WINDOW_SECONDS, computeSettlementWindow, deriveCycleDates, nextCycleBoundary, } from './settlement';
|
|
21
30
|
export { getTrancheDisplayName, UPPER_MEZZANINE } from './tranche-display-name';
|
|
22
31
|
export { compareTrancheSeniority, derivePoolStatus, MIN_TRANCHE_CAPACITY, netTrancheApyBounds, pickDefaultTrancheId, poolAllTranchesFull, trancheApyBounds, trancheHasCapacity, trancheRiskRank, } from './tranches';
|
|
32
|
+
export { classifyWalletFailure, isUnpredictableGas, isUserRejected, } from './wallet-errors';
|
|
23
33
|
//# sourceMappingURL=index.js.map
|
package/dist/domain/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/domain/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/domain/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EACH,cAAc,EACd,cAAc,EACd,cAAc,EACd,eAAe,GAClB,MAAM,SAAS,CAAC;AAEjB,OAAO,EACH,SAAS,EACT,2BAA2B,EAC3B,kBAAkB,EAClB,cAAc,EACd,iBAAiB,EACjB,eAAe,EACf,eAAe,GAClB,MAAM,cAAc,CAAC;AAGtB,OAAO,EACH,WAAW,EACX,YAAY,EACZ,sBAAsB,EACtB,2BAA2B,EAC3B,2BAA2B,EAC3B,iBAAiB,EACjB,qBAAqB,EACrB,oBAAoB,GACvB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACH,MAAM,EACN,mBAAmB,EACnB,sBAAsB,EACtB,WAAW,EACX,aAAa,GAChB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACH,cAAc,EACd,wBAAwB,EACxB,2BAA2B,EAC3B,iCAAiC,EACjC,6BAA6B,EAC7B,iBAAiB,EACjB,sBAAsB,EACtB,qBAAqB,GACxB,MAAM,iBAAiB,CAAC;AAYzB,OAAO,EACH,iBAAiB,EACjB,uBAAuB,EACvB,UAAU,EACV,kBAAkB,GACrB,MAAM,SAAS,CAAC;AAGjB,OAAO,EACH,gBAAgB,EAChB,kBAAkB,EAClB,wBAAwB,EACxB,aAAa,EACb,kBAAkB,EAClB,gBAAgB,GACnB,MAAM,YAAY,CAAC;AAOpB,OAAO,EACH,uBAAuB,EACvB,uBAAuB,EACvB,gBAAgB,EAChB,iBAAiB,GACpB,MAAM,cAAc,CAAC;AAOtB,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEhF,OAAO,EACH,uBAAuB,EACvB,gBAAgB,EAChB,oBAAoB,EACpB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,GAClB,MAAM,YAAY,CAAC;AAOpB,OAAO,EACH,qBAAqB,EACrB,kBAAkB,EAClB,cAAc,GACjB,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loan-contract protocol: the strings and the bytes kasu-backend verifies.
|
|
3
|
+
*
|
|
4
|
+
* ⚠️ THE ONE EXCEPTION TO "NUMBERS AND CODES ONLY". Everything this module
|
|
5
|
+
* returns as a string is a PROTOCOL string, not copy. The backend reconstructs
|
|
6
|
+
* each of these messages byte-for-byte and verifies the lender's signature
|
|
7
|
+
* against it; a changed word, separator, line break or date format does not
|
|
8
|
+
* read differently — it stops every signature verifying. They are here, in the
|
|
9
|
+
* shared layer, for exactly the reason `getTrancheDisplayName` is: so the
|
|
10
|
+
* applications cannot drift apart on them.
|
|
11
|
+
*
|
|
12
|
+
* Any change to a builder below is a coordinated multi-repo change that has to
|
|
13
|
+
* land in kasu-backend at the same moment. Do not "tidy" this file.
|
|
14
|
+
*
|
|
15
|
+
* Lifted from kasu-ui's `features/lending/lib/{contract-types, sign-message}.ts`
|
|
16
|
+
* and `encode-deposit-data.ts` — the last rewritten from viem to ethers v5
|
|
17
|
+
* (kasu-mobile already runs that port; the tests pin the two byte-identical).
|
|
18
|
+
*/
|
|
19
|
+
/** Recursive list nesting: `list-N-list`, etc. — indexed by string keys. */
|
|
20
|
+
export type ContractListItem = {
|
|
21
|
+
label?: string;
|
|
22
|
+
description?: string;
|
|
23
|
+
} & Record<string, unknown>;
|
|
24
|
+
export type ContractSection = {
|
|
25
|
+
title?: string;
|
|
26
|
+
description?: string;
|
|
27
|
+
} & Record<`list-${number}`, ContractListItem | undefined> & Record<string, unknown>;
|
|
28
|
+
export type RetailLoanContract = {
|
|
29
|
+
important?: {
|
|
30
|
+
title?: string;
|
|
31
|
+
description?: string;
|
|
32
|
+
};
|
|
33
|
+
intro?: string;
|
|
34
|
+
between?: string;
|
|
35
|
+
parties?: ContractSection;
|
|
36
|
+
background?: ContractSection;
|
|
37
|
+
witnesses?: ContractSection;
|
|
38
|
+
} & Record<`subheader-${number}`, ContractSection | undefined> & Record<string, unknown>;
|
|
39
|
+
/**
|
|
40
|
+
* The exempt (wholesale) contract renders from the same tree as the retail
|
|
41
|
+
* one; the templates differ, the SHAPE does not. Kept as its own name because
|
|
42
|
+
* `contractType` distinguishes them everywhere else.
|
|
43
|
+
*/
|
|
44
|
+
export type ExemptLoanContract = RetailLoanContract;
|
|
45
|
+
/** Either contract, parsed. Both are the same tree. */
|
|
46
|
+
export type LoanContractFormatted = RetailLoanContract;
|
|
47
|
+
/**
|
|
48
|
+
* `contractType` arrives over the wire as a free string; these are the two the
|
|
49
|
+
* version byte encodes.
|
|
50
|
+
*/
|
|
51
|
+
export type ContractType = 'retail' | 'exempt';
|
|
52
|
+
export interface GenerateContractResponse {
|
|
53
|
+
fullName: string;
|
|
54
|
+
/** The plaintext the lender signs (EIP-191). */
|
|
55
|
+
contractMessage: string;
|
|
56
|
+
/** Returned as a JSON-encoded string; parse before rendering. */
|
|
57
|
+
formattedMessage: string;
|
|
58
|
+
contractType: ContractType;
|
|
59
|
+
/** Template version (>= 1). */
|
|
60
|
+
contractVersion: number;
|
|
61
|
+
/** ms-epoch; feeds the on-chain `depositData` and the TTL guard. */
|
|
62
|
+
timestamp: number;
|
|
63
|
+
}
|
|
64
|
+
export type ResolvedContractResponse = GenerateContractResponse & {
|
|
65
|
+
isValid: boolean;
|
|
66
|
+
};
|
|
67
|
+
/** Narrow the backend's loose `contractType` string to the encoded union. */
|
|
68
|
+
export declare function asContractType(raw: string): ContractType;
|
|
69
|
+
/**
|
|
70
|
+
* Parse the server's JSON-string `formattedMessage` into a tree. Returns
|
|
71
|
+
* `null` on parse failure so a renderer can fall back to the plaintext.
|
|
72
|
+
*/
|
|
73
|
+
export declare function parseFormattedMessage(raw: string): LoanContractFormatted | null;
|
|
74
|
+
/**
|
|
75
|
+
* Pack the contract version and type into the `versionType` word.
|
|
76
|
+
*
|
|
77
|
+
* ```
|
|
78
|
+
* high byte = contract version (>= 1)
|
|
79
|
+
* low byte = 0 for retail, 1 for exempt
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
export declare function buildContractVersionType(contractVersion: number, contractType: ContractType): number;
|
|
83
|
+
/**
|
|
84
|
+
* Build the on-chain `depositData` blob that `requestDepositWithKyc` expects.
|
|
85
|
+
*
|
|
86
|
+
* The KasuController decodes the bytes as
|
|
87
|
+
* `(bytes signature, uint256 timestamp, uint256 versionType)` and uses the
|
|
88
|
+
* embedded acceptance signature to verify — retrospectively, via the
|
|
89
|
+
* agreements service `/contract/resolve` — that the lender signed the
|
|
90
|
+
* loan-contract text. The ABI tuple and the packing are consensus-critical:
|
|
91
|
+
* these bytes go on chain.
|
|
92
|
+
*
|
|
93
|
+
* kasu-ui encodes this with viem, kasu-mobile with ethers v5 (viem is not
|
|
94
|
+
* available on Expo). This is the ethers v5 implementation, and
|
|
95
|
+
* `loan-contract.test.ts` pins its output byte-for-byte against fixtures
|
|
96
|
+
* produced by the viem version, so the two apps can never diverge here.
|
|
97
|
+
*
|
|
98
|
+
* @param args.signature EIP-191 signature from the lender accepting
|
|
99
|
+
* `contractMessage`, as a 0x-prefixed hex string.
|
|
100
|
+
* @param args.timestamp ms-epoch from the contract response.
|
|
101
|
+
*/
|
|
102
|
+
export declare function encodeDepositData(args: {
|
|
103
|
+
signature: string;
|
|
104
|
+
timestamp: number;
|
|
105
|
+
contractVersion: number;
|
|
106
|
+
contractType: ContractType;
|
|
107
|
+
}): string;
|
|
108
|
+
/**
|
|
109
|
+
* Format a unix timestamp as `{day} {MonthName} {yyyy}, {HH}:{mm}` in UTC.
|
|
110
|
+
* Day is non-padded; hour and minute are zero-padded to two digits (24h). A
|
|
111
|
+
* timestamp with >= 13 digits is treated as milliseconds, otherwise as seconds
|
|
112
|
+
* — the same auto-detection kasu-backend applies.
|
|
113
|
+
*
|
|
114
|
+
* Deliberately a manual formatter with English month names: no locale, no
|
|
115
|
+
* `Intl`, so the output is byte-identical across runtimes and time zones. This
|
|
116
|
+
* is not a display date. It goes inside a signed message.
|
|
117
|
+
*
|
|
118
|
+
* e.g. 1785313320000 → `"29 July 2026, 08:22"`
|
|
119
|
+
*/
|
|
120
|
+
export declare function formatSignTimestampUtc(timestamp: number): string;
|
|
121
|
+
/**
|
|
122
|
+
* The 4-line human-readable message a lender signs to generate their loan
|
|
123
|
+
* agreement for review — `POST /contract/generate`.
|
|
124
|
+
*
|
|
125
|
+
* ⚠️ BYTE-EXACT PROTOCOL STRING. kasu-backend rebuilds this string from the
|
|
126
|
+
* request body and verifies the signature against it, so the wording,
|
|
127
|
+
* ordering, separators, line breaks and date format are all part of the wire
|
|
128
|
+
* contract. The separator between the line-2 fields is a MIDDLE DOT U+00B7
|
|
129
|
+
* (·) with a single space on each side; the four lines are joined with `\n`.
|
|
130
|
+
*
|
|
131
|
+
* The backend takes this format only when all four display fields are present
|
|
132
|
+
* and non-empty, and it cross-checks `amountLabel`'s leading number against
|
|
133
|
+
* the `depositAmount` it was sent (thousands separators stripped) — a message
|
|
134
|
+
* that states an amount other than the one being executed is refused.
|
|
135
|
+
*/
|
|
136
|
+
export declare function buildLoanAgreementSignMessage(p: {
|
|
137
|
+
strategyName: string;
|
|
138
|
+
region: string;
|
|
139
|
+
optionName: string;
|
|
140
|
+
amountLabel: string;
|
|
141
|
+
timestamp: number;
|
|
142
|
+
}): string;
|
|
143
|
+
/**
|
|
144
|
+
* The legacy `/contract/generate` and `/contract/resolve` message.
|
|
145
|
+
*
|
|
146
|
+
* ⚠️ BYTE-EXACT PROTOCOL STRING. kasu-backend rebuilds it as
|
|
147
|
+
* `` `I request contract content for ${address} at ${timestamp}.` `` from the
|
|
148
|
+
* `address` and `timestamp` fields of the request body — so the string signed
|
|
149
|
+
* and the body sent must agree exactly, INCLUDING the address casing. This
|
|
150
|
+
* builder lowercases, and the request body must carry the same lowercased
|
|
151
|
+
* address; that is what both apps signing this format do today.
|
|
152
|
+
*
|
|
153
|
+
* The backend takes this path whenever the four human-readable display fields
|
|
154
|
+
* are absent, and documents it as permanent until the legacy app is
|
|
155
|
+
* decommissioned. `/contract/resolve` has no other format — every consumer
|
|
156
|
+
* signs this one to retrieve an existing agreement.
|
|
157
|
+
*
|
|
158
|
+
* @param timestampMs ms-epoch, and the same value sent as the body's
|
|
159
|
+
* `timestamp`.
|
|
160
|
+
*/
|
|
161
|
+
export declare function buildLegacyContractRequestMessage(address: string, timestampMs: number): string;
|
|
162
|
+
/**
|
|
163
|
+
* The `POST /contract/fullname` message.
|
|
164
|
+
*
|
|
165
|
+
* ⚠️ BYTE-EXACT PROTOCOL STRING, on the same terms as
|
|
166
|
+
* `buildLegacyContractRequestMessage`: kasu-backend rebuilds
|
|
167
|
+
* `` `I request my full name for ${address} at ${timestamp}.` `` from the
|
|
168
|
+
* request body and verifies the signature against it, so the body must carry
|
|
169
|
+
* the same lowercased address this builder signs.
|
|
170
|
+
*
|
|
171
|
+
* @param timestampMs ms-epoch, and the same value sent as the body's
|
|
172
|
+
* `timestamp`.
|
|
173
|
+
*/
|
|
174
|
+
export declare function buildFullNameRequestMessage(address: string, timestampMs: number): string;
|