kerb-sdk 0.1.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 +12 -0
- package/index.d.ts +175 -0
- package/index.js +92 -0
- package/package.json +16 -0
package/README.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# kerb-sdk
|
|
2
|
+
|
|
3
|
+
Read Kerb Terms from anywhere: the market-time risk layer for tokenized stocks on X Layer.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { Kerb } from "kerb-sdk";
|
|
7
|
+
const kerb = new Kerb(); // X Layer mainnet, https://api.usekerb.xyz
|
|
8
|
+
const t = await kerb.terms("BRK.Bx");
|
|
9
|
+
if (!t.usable) throw new Error("no new exposure: terms stale or regime unsound");
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Docs: https://www.usekerb.xyz/docs · API: https://github.com/Franlinozz/Kerb/blob/main/docs/API.md · MIT
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @kerb/sdk: read Kerb Terms from the REST API or straight from the chain.
|
|
3
|
+
*
|
|
4
|
+
* const kerb = new Kerb();
|
|
5
|
+
* const terms = await kerb.terms("KOx");
|
|
6
|
+
* if (!terms.usable) return; // no new risk may be taken
|
|
7
|
+
* const maxDebt = Number(terms.carryLTV) * collateralValue;
|
|
8
|
+
*
|
|
9
|
+
* Every number is a decimal string and carries the provenance label it was published with.
|
|
10
|
+
*/
|
|
11
|
+
export type ProvenanceLabel = "Verified" | "Observed" | "Attested" | "Computed";
|
|
12
|
+
export declare const KERB_API = "https://api.usekerb.xyz";
|
|
13
|
+
export declare const X_LAYER = 196;
|
|
14
|
+
export declare const X_LAYER_TESTNET = 1952;
|
|
15
|
+
export declare const REGIMES: readonly ["DEEP", "NORMAL", "THIN", "PRE_TRANSITION", "REFERENCE_CLOSED", "ACTION", "HALTED", "STALE", "RECOVERY"];
|
|
16
|
+
export type Regime = (typeof REGIMES)[number];
|
|
17
|
+
export interface RawValue {
|
|
18
|
+
raw: string;
|
|
19
|
+
decimals: number;
|
|
20
|
+
label: ProvenanceLabel;
|
|
21
|
+
}
|
|
22
|
+
export interface Terms {
|
|
23
|
+
chainId: number;
|
|
24
|
+
assetId: string;
|
|
25
|
+
symbol: string | null;
|
|
26
|
+
observedAt: string;
|
|
27
|
+
ageSec: number;
|
|
28
|
+
/** false means "no new risk may be taken". It never means "liquidate everything". */
|
|
29
|
+
usable: boolean;
|
|
30
|
+
regime: {
|
|
31
|
+
value: Regime;
|
|
32
|
+
index: number;
|
|
33
|
+
label: ProvenanceLabel;
|
|
34
|
+
};
|
|
35
|
+
creditMark: RawValue;
|
|
36
|
+
carryLTV: RawValue;
|
|
37
|
+
sessionMaxLTV: RawValue;
|
|
38
|
+
debtCeiling: RawValue;
|
|
39
|
+
executableDepth1: RawValue;
|
|
40
|
+
inputsHash: string;
|
|
41
|
+
tx: string;
|
|
42
|
+
contracts: {
|
|
43
|
+
clock?: string;
|
|
44
|
+
terms?: string;
|
|
45
|
+
};
|
|
46
|
+
history: {
|
|
47
|
+
observedAt: string;
|
|
48
|
+
regime: Regime;
|
|
49
|
+
carryLTV: string;
|
|
50
|
+
sessionMaxLTV: string;
|
|
51
|
+
creditMark: string;
|
|
52
|
+
tx: string;
|
|
53
|
+
}[];
|
|
54
|
+
}
|
|
55
|
+
export interface BoardRow {
|
|
56
|
+
symbol: string;
|
|
57
|
+
underlying: {
|
|
58
|
+
symbol: string;
|
|
59
|
+
market: string;
|
|
60
|
+
currency: string;
|
|
61
|
+
};
|
|
62
|
+
status: "live" | "no report" | "stale";
|
|
63
|
+
regime: {
|
|
64
|
+
value: Regime | null;
|
|
65
|
+
label: ProvenanceLabel;
|
|
66
|
+
observedAt: string | null;
|
|
67
|
+
};
|
|
68
|
+
creditMark: {
|
|
69
|
+
value: string | null;
|
|
70
|
+
label: ProvenanceLabel;
|
|
71
|
+
};
|
|
72
|
+
executableDepth1: {
|
|
73
|
+
value: string | null;
|
|
74
|
+
label: ProvenanceLabel;
|
|
75
|
+
};
|
|
76
|
+
carryLTV: {
|
|
77
|
+
value: string | null;
|
|
78
|
+
label: ProvenanceLabel;
|
|
79
|
+
};
|
|
80
|
+
sessionMaxLTV: {
|
|
81
|
+
value: string | null;
|
|
82
|
+
label: ProvenanceLabel;
|
|
83
|
+
};
|
|
84
|
+
debtCeiling: {
|
|
85
|
+
value: string | null;
|
|
86
|
+
label: ProvenanceLabel;
|
|
87
|
+
};
|
|
88
|
+
coverageRatio: {
|
|
89
|
+
value: string | null;
|
|
90
|
+
label: ProvenanceLabel;
|
|
91
|
+
};
|
|
92
|
+
reportAgeSec: number | null;
|
|
93
|
+
}
|
|
94
|
+
export interface Board {
|
|
95
|
+
chainId: number;
|
|
96
|
+
loanAsset: string;
|
|
97
|
+
generatedAt: string;
|
|
98
|
+
contracts: {
|
|
99
|
+
KerbClock: string | null;
|
|
100
|
+
KerbTerms: string | null;
|
|
101
|
+
};
|
|
102
|
+
rows: BoardRow[];
|
|
103
|
+
sources: {
|
|
104
|
+
name: string;
|
|
105
|
+
lastObservedAt: string | null;
|
|
106
|
+
ageSec: number | null;
|
|
107
|
+
healthy: boolean;
|
|
108
|
+
}[];
|
|
109
|
+
}
|
|
110
|
+
/** Scale a raw integer string by its decimals, exactly, without floating point. */
|
|
111
|
+
export declare function toDecimalString(v: RawValue | {
|
|
112
|
+
raw: string;
|
|
113
|
+
decimals: number;
|
|
114
|
+
}): string;
|
|
115
|
+
export declare class KerbError extends Error {
|
|
116
|
+
readonly status: number;
|
|
117
|
+
constructor(status: number, message: string);
|
|
118
|
+
}
|
|
119
|
+
export interface KerbOptions {
|
|
120
|
+
baseUrl?: string;
|
|
121
|
+
chainId?: number;
|
|
122
|
+
fetch?: typeof globalThis.fetch;
|
|
123
|
+
timeoutMs?: number;
|
|
124
|
+
}
|
|
125
|
+
export declare class Kerb {
|
|
126
|
+
private readonly baseUrl;
|
|
127
|
+
private readonly chainId;
|
|
128
|
+
private readonly doFetch;
|
|
129
|
+
private readonly timeoutMs;
|
|
130
|
+
constructor(opts?: KerbOptions);
|
|
131
|
+
private get;
|
|
132
|
+
/** Latest posted Terms for an asset, by symbol, token address or assetId. */
|
|
133
|
+
terms(asset: string, chainId?: number): Promise<Terms>;
|
|
134
|
+
/** Every tracked asset with its regime, mark, depth and capacities. */
|
|
135
|
+
board(chainId?: number): Promise<Board>;
|
|
136
|
+
/** The pinned input bundle behind a report, so any number can be recomputed. */
|
|
137
|
+
bundle(inputsHashOrReportId: string): Promise<unknown>;
|
|
138
|
+
report(reportId: string): Promise<unknown>;
|
|
139
|
+
health(): Promise<{
|
|
140
|
+
status: string;
|
|
141
|
+
observations: {
|
|
142
|
+
ageSec: number | null;
|
|
143
|
+
};
|
|
144
|
+
}>;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Read `effectiveTerms` straight from KerbTerms with any viem-compatible client, for
|
|
148
|
+
* consumers that will not trust an HTTP API.
|
|
149
|
+
*/
|
|
150
|
+
export declare const KERB_TERMS_ABI: readonly [{
|
|
151
|
+
readonly type: "function";
|
|
152
|
+
readonly name: "effectiveTerms";
|
|
153
|
+
readonly stateMutability: "view";
|
|
154
|
+
readonly inputs: readonly [{
|
|
155
|
+
readonly name: "assetId";
|
|
156
|
+
readonly type: "bytes32";
|
|
157
|
+
}];
|
|
158
|
+
readonly outputs: readonly [{
|
|
159
|
+
readonly name: "carryLTV";
|
|
160
|
+
readonly type: "uint64";
|
|
161
|
+
}, {
|
|
162
|
+
readonly name: "sessionMaxLTV";
|
|
163
|
+
readonly type: "uint64";
|
|
164
|
+
}, {
|
|
165
|
+
readonly name: "creditMark";
|
|
166
|
+
readonly type: "uint128";
|
|
167
|
+
}, {
|
|
168
|
+
readonly name: "regime";
|
|
169
|
+
readonly type: "uint16";
|
|
170
|
+
}, {
|
|
171
|
+
readonly name: "usable";
|
|
172
|
+
readonly type: "bool";
|
|
173
|
+
}];
|
|
174
|
+
}];
|
|
175
|
+
export declare function regimeName(index: number): Regime;
|
package/index.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @kerb/sdk: read Kerb Terms from the REST API or straight from the chain.
|
|
3
|
+
*
|
|
4
|
+
* const kerb = new Kerb();
|
|
5
|
+
* const terms = await kerb.terms("KOx");
|
|
6
|
+
* if (!terms.usable) return; // no new risk may be taken
|
|
7
|
+
* const maxDebt = Number(terms.carryLTV) * collateralValue;
|
|
8
|
+
*
|
|
9
|
+
* Every number is a decimal string and carries the provenance label it was published with.
|
|
10
|
+
*/
|
|
11
|
+
export const KERB_API = "https://api.usekerb.xyz";
|
|
12
|
+
export const X_LAYER = 196;
|
|
13
|
+
export const X_LAYER_TESTNET = 1952;
|
|
14
|
+
export const REGIMES = [
|
|
15
|
+
"DEEP", "NORMAL", "THIN", "PRE_TRANSITION", "REFERENCE_CLOSED", "ACTION", "HALTED", "STALE", "RECOVERY",
|
|
16
|
+
];
|
|
17
|
+
/** Scale a raw integer string by its decimals, exactly, without floating point. */
|
|
18
|
+
export function toDecimalString(v) {
|
|
19
|
+
const neg = v.raw.startsWith("-");
|
|
20
|
+
const digits = (neg ? v.raw.slice(1) : v.raw).padStart(v.decimals + 1, "0");
|
|
21
|
+
const cut = digits.length - v.decimals;
|
|
22
|
+
const out = `${digits.slice(0, cut)}.${digits.slice(cut)}`.replace(/\.?0+$/, "");
|
|
23
|
+
return `${neg ? "-" : ""}${out === "" ? "0" : out}`;
|
|
24
|
+
}
|
|
25
|
+
export class KerbError extends Error {
|
|
26
|
+
status;
|
|
27
|
+
constructor(status, message) {
|
|
28
|
+
super(message);
|
|
29
|
+
this.status = status;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export class Kerb {
|
|
33
|
+
baseUrl;
|
|
34
|
+
chainId;
|
|
35
|
+
doFetch;
|
|
36
|
+
timeoutMs;
|
|
37
|
+
constructor(opts = {}) {
|
|
38
|
+
this.baseUrl = (opts.baseUrl ?? KERB_API).replace(/\/$/, "");
|
|
39
|
+
this.chainId = opts.chainId ?? X_LAYER;
|
|
40
|
+
this.doFetch = opts.fetch ?? globalThis.fetch;
|
|
41
|
+
this.timeoutMs = opts.timeoutMs ?? 10_000;
|
|
42
|
+
}
|
|
43
|
+
async get(path) {
|
|
44
|
+
const res = await this.doFetch(`${this.baseUrl}${path}`, { signal: AbortSignal.timeout(this.timeoutMs) });
|
|
45
|
+
if (!res.ok)
|
|
46
|
+
throw new KerbError(res.status, `kerb: ${path} returned ${res.status}`);
|
|
47
|
+
return (await res.json());
|
|
48
|
+
}
|
|
49
|
+
/** Latest posted Terms for an asset, by symbol, token address or assetId. */
|
|
50
|
+
async terms(asset, chainId = this.chainId) {
|
|
51
|
+
return this.get(`/v1/terms/${chainId}/${encodeURIComponent(asset)}`);
|
|
52
|
+
}
|
|
53
|
+
/** Every tracked asset with its regime, mark, depth and capacities. */
|
|
54
|
+
async board(chainId = this.chainId) {
|
|
55
|
+
return this.get(`/v1/board?chain=${chainId}`);
|
|
56
|
+
}
|
|
57
|
+
/** The pinned input bundle behind a report, so any number can be recomputed. */
|
|
58
|
+
async bundle(inputsHashOrReportId) {
|
|
59
|
+
return this.get(`/v1/bundle/${encodeURIComponent(inputsHashOrReportId)}`);
|
|
60
|
+
}
|
|
61
|
+
async report(reportId) {
|
|
62
|
+
return this.get(`/v1/reports/${encodeURIComponent(reportId)}`);
|
|
63
|
+
}
|
|
64
|
+
async health() {
|
|
65
|
+
return this.get("/health");
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Read `effectiveTerms` straight from KerbTerms with any viem-compatible client, for
|
|
70
|
+
* consumers that will not trust an HTTP API.
|
|
71
|
+
*/
|
|
72
|
+
export const KERB_TERMS_ABI = [
|
|
73
|
+
{
|
|
74
|
+
type: "function",
|
|
75
|
+
name: "effectiveTerms",
|
|
76
|
+
stateMutability: "view",
|
|
77
|
+
inputs: [{ name: "assetId", type: "bytes32" }],
|
|
78
|
+
outputs: [
|
|
79
|
+
{ name: "carryLTV", type: "uint64" },
|
|
80
|
+
{ name: "sessionMaxLTV", type: "uint64" },
|
|
81
|
+
{ name: "creditMark", type: "uint128" },
|
|
82
|
+
{ name: "regime", type: "uint16" },
|
|
83
|
+
{ name: "usable", type: "bool" },
|
|
84
|
+
],
|
|
85
|
+
},
|
|
86
|
+
];
|
|
87
|
+
export function regimeName(index) {
|
|
88
|
+
const r = REGIMES[index];
|
|
89
|
+
if (!r)
|
|
90
|
+
throw new KerbError(0, `unknown regime index ${index}`);
|
|
91
|
+
return r;
|
|
92
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "kerb-sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Read Kerb Terms: the market-time risk layer for tokenized stocks on X Layer. Credit on the market's clock.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"types": "index.d.ts",
|
|
8
|
+
"exports": { ".": { "types": "./index.d.ts", "import": "./index.js" } },
|
|
9
|
+
"files": ["index.js", "index.d.ts", "README.md"],
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"homepage": "https://www.usekerb.xyz/developers",
|
|
12
|
+
"repository": { "type": "git", "url": "https://github.com/Franlinozz/Kerb", "directory": "packages/sdk" },
|
|
13
|
+
"keywords": ["kerb", "x layer", "tokenized stocks", "xstocks", "credit", "risk", "okx"],
|
|
14
|
+
"peerDependencies": { "viem": "^2.45.0" },
|
|
15
|
+
"peerDependenciesMeta": { "viem": { "optional": true } }
|
|
16
|
+
}
|