@ixo/oracle-runtime-workers 0.29.0 → 0.30.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/package.json +2 -2
- package/src/plugins/hub-actions/hub-actions.plugin.ts +73 -24
- package/src/plugins/ixo-chain/balances.ts +141 -0
- package/src/plugins/ixo-chain/blocksync.ts +11 -6
- package/src/plugins/ixo-chain/denoms.ts +84 -36
- package/src/plugins/ixo-chain/http.ts +300 -63
- package/src/plugins/ixo-chain/ixo-chain.plugin.ts +182 -301
- package/src/plugins/ixo-chain/kinds/chain/account.ts +516 -0
- package/src/plugins/ixo-chain/kinds/chain/claims.ts +287 -0
- package/src/plugins/ixo-chain/kinds/chain/economics.ts +237 -0
- package/src/plugins/ixo-chain/kinds/chain/governance.ts +455 -0
- package/src/plugins/ixo-chain/kinds/chain/grants.ts +682 -0
- package/src/plugins/ixo-chain/kinds/chain/index.ts +23 -0
- package/src/plugins/ixo-chain/kinds/chain/liquid-staking.ts +279 -0
- package/src/plugins/ixo-chain/kinds/chain/shared.ts +286 -0
- package/src/plugins/ixo-chain/kinds/chain/staking.ts +220 -0
- package/src/plugins/ixo-chain/kinds/coins.ts +112 -0
- package/src/plugins/ixo-chain/kinds/core.ts +246 -0
- package/src/plugins/ixo-chain/kinds/indexed/agent-deposits.ts +124 -0
- package/src/plugins/ixo-chain/kinds/indexed/dao-proposals.ts +379 -0
- package/src/plugins/ixo-chain/kinds/indexed/daos.ts +319 -0
- package/src/plugins/ixo-chain/kinds/indexed/disputes.ts +159 -0
- package/src/plugins/ixo-chain/kinds/indexed/evaluations.ts +172 -0
- package/src/plugins/ixo-chain/kinds/indexed/index.ts +21 -0
- package/src/plugins/ixo-chain/kinds/indexed/liquid-stake-history.ts +160 -0
- package/src/plugins/ixo-chain/kinds/indexed/member-budgets.ts +175 -0
- package/src/plugins/ixo-chain/kinds/indexed/shared.ts +211 -0
- package/src/plugins/ixo-chain/kinds/indexed/token-history.ts +205 -0
- package/src/plugins/ixo-chain/kinds/registry.ts +43 -0
- package/src/plugins/ixo-chain/kinds/types.ts +156 -0
- package/src/plugins/ixo-chain/lookup.ts +470 -0
- package/src/plugins/ixo-chain/overview.ts +1060 -0
- package/src/plugins/ixo-chain/queries.ts +170 -138
- package/src/plugins/ixo-chain/rest.ts +129 -54
- package/src/plugins/ixo-chain/summaries.ts +3 -2
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
* Chain REST (the node's LCD gateway) reads: one GET, bounded and parsed,
|
|
3
3
|
* under the shared retry policy.
|
|
4
4
|
*/
|
|
5
|
-
import
|
|
5
|
+
import { z } from 'zod';
|
|
6
6
|
import {
|
|
7
7
|
exchange,
|
|
8
8
|
unavailable,
|
|
9
9
|
withRetry,
|
|
10
|
+
type Attempt,
|
|
10
11
|
type ChainCall,
|
|
11
12
|
type Outcome,
|
|
12
13
|
} from './http';
|
|
@@ -15,65 +16,139 @@ export const REST_MAX_BYTES = 512 * 1024;
|
|
|
15
16
|
/** Per-attempt wall bound for one REST read. */
|
|
16
17
|
export const REST_ATTEMPT_TIMEOUT_MS = 8_000;
|
|
17
18
|
|
|
19
|
+
/** How one REST read is bounded, and which answers mean "none". */
|
|
20
|
+
export interface RestOptions {
|
|
21
|
+
/** Names the read in log lines (never the path: it carries an address). */
|
|
22
|
+
label: string;
|
|
23
|
+
timeoutMs?: number;
|
|
24
|
+
maxBytes?: number;
|
|
25
|
+
/** A 404 is a quiet `not_found` failure instead of `unavailable`. */
|
|
26
|
+
notFoundOn404?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* HTTP statuses that mean "there is none" for this resource (e.g.
|
|
29
|
+
* `[400, 404]` for a vote never cast): such an answer is `Absent`, an
|
|
30
|
+
* `ok` result, neither retried nor logged. Meant for 4xx statuses; it
|
|
31
|
+
* takes precedence over `notFoundOn404`. A 400 counts only when its body
|
|
32
|
+
* is a gRPC-gateway error with code 3 (InvalidArgument) or 5 (NotFound);
|
|
33
|
+
* any other 400 is a failure.
|
|
34
|
+
*/
|
|
35
|
+
noneOn?: readonly number[];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** gRPC InvalidArgument and NotFound: the codes of a gateway 400 that says "there is none". */
|
|
39
|
+
const NONE_GRPC_CODES: readonly number[] = [3, 5];
|
|
40
|
+
const grpcError = z.object({ code: z.number() });
|
|
41
|
+
|
|
42
|
+
/** Whether `answer` is one of the `noneOn` statuses, a 400 only with a "none" gRPC code. */
|
|
43
|
+
function isNone(
|
|
44
|
+
answer: Extract<Attempt<string>, { ok: false }>,
|
|
45
|
+
noneOn: readonly number[] | undefined,
|
|
46
|
+
): boolean {
|
|
47
|
+
if (answer.status === undefined || !noneOn?.includes(answer.status))
|
|
48
|
+
return false;
|
|
49
|
+
if (answer.status !== 400) return true;
|
|
50
|
+
if (answer.errorBody === undefined) return false;
|
|
51
|
+
let body: unknown;
|
|
52
|
+
try {
|
|
53
|
+
body = JSON.parse(answer.errorBody);
|
|
54
|
+
} catch {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
const parsed = grpcError.safeParse(body);
|
|
58
|
+
return parsed.success && NONE_GRPC_CODES.includes(parsed.data.code);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** The answer of a read whose upstream answered one of its `noneOn` statuses. */
|
|
62
|
+
export type Absent = { ok: true; absent: true };
|
|
63
|
+
|
|
64
|
+
/** What one attempt found: the parsed body, or a "none" status. */
|
|
65
|
+
type Found<T> = { absent: false; value: T } | { absent: true };
|
|
66
|
+
|
|
18
67
|
/**
|
|
19
|
-
* `GET ${restUrl}${path}` parsed by `schema`.
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
68
|
+
* `GET ${restUrl}${path}` parsed by `schema`. A body that is not JSON or
|
|
69
|
+
* not the expected shape is `unavailable` and is not retried. A 404 is
|
|
70
|
+
* `unavailable` unless `notFoundOn404` (a resource that may be absent); a
|
|
71
|
+
* status in `noneOn` is {@link Absent} (a 400 only with a gRPC code 3 or
|
|
72
|
+
* 5, read from at most 8 KiB of its body).
|
|
23
73
|
*/
|
|
24
74
|
export function restJson<S extends z.ZodType>(
|
|
25
75
|
call: ChainCall,
|
|
26
76
|
path: string,
|
|
27
77
|
schema: S,
|
|
28
|
-
opts: {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
78
|
+
opts: RestOptions & { noneOn?: undefined },
|
|
79
|
+
): Promise<Outcome<z.infer<S>>>;
|
|
80
|
+
export function restJson<S extends z.ZodType>(
|
|
81
|
+
call: ChainCall,
|
|
82
|
+
path: string,
|
|
83
|
+
schema: S,
|
|
84
|
+
opts: RestOptions & { noneOn: readonly number[] },
|
|
85
|
+
): Promise<Outcome<z.infer<S>> | Absent>;
|
|
86
|
+
export function restJson<S extends z.ZodType>(
|
|
87
|
+
call: ChainCall,
|
|
88
|
+
path: string,
|
|
89
|
+
schema: S,
|
|
90
|
+
opts: RestOptions,
|
|
91
|
+
): Promise<Outcome<z.infer<S>> | Absent>;
|
|
92
|
+
export async function restJson<S extends z.ZodType>(
|
|
93
|
+
call: ChainCall,
|
|
94
|
+
path: string,
|
|
95
|
+
schema: S,
|
|
96
|
+
opts: RestOptions,
|
|
97
|
+
): Promise<Outcome<z.infer<S>> | Absent> {
|
|
35
98
|
const base = call.restUrl.replace(/\/+$/, '');
|
|
36
99
|
const url = `${base}${path}`;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
100
|
+
const read = await withRetry<Found<z.infer<S>>>(
|
|
101
|
+
call,
|
|
102
|
+
{ label: opts.label, upstream: base },
|
|
103
|
+
async () => {
|
|
104
|
+
const answer = await exchange(
|
|
105
|
+
call,
|
|
106
|
+
'chain',
|
|
107
|
+
url,
|
|
108
|
+
{ method: 'GET', headers: { accept: 'application/json' } },
|
|
109
|
+
{
|
|
110
|
+
timeoutMs: opts.timeoutMs ?? REST_ATTEMPT_TIMEOUT_MS,
|
|
111
|
+
maxBytes: opts.maxBytes ?? REST_MAX_BYTES,
|
|
112
|
+
notFoundOn404: opts.notFoundOn404 ?? false,
|
|
113
|
+
...(opts.noneOn?.includes(400) ? { errorBodyOn: [400] } : {}),
|
|
114
|
+
},
|
|
115
|
+
);
|
|
116
|
+
if (!answer.ok)
|
|
117
|
+
return isNone(answer, opts.noneOn)
|
|
118
|
+
? { ok: true, value: { absent: true } }
|
|
119
|
+
: answer;
|
|
120
|
+
let body: unknown;
|
|
121
|
+
try {
|
|
122
|
+
body = JSON.parse(answer.value);
|
|
123
|
+
} catch {
|
|
124
|
+
return {
|
|
125
|
+
ok: false,
|
|
126
|
+
failure: unavailable(
|
|
127
|
+
'chain',
|
|
128
|
+
'it answered something that is not JSON',
|
|
129
|
+
false,
|
|
130
|
+
),
|
|
131
|
+
retry: false,
|
|
132
|
+
note: 'malformed JSON',
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
const parsed = schema.safeParse(body);
|
|
136
|
+
if (!parsed.success)
|
|
137
|
+
return {
|
|
138
|
+
ok: false,
|
|
139
|
+
failure: unavailable(
|
|
140
|
+
'chain',
|
|
141
|
+
'it answered in an unexpected shape',
|
|
142
|
+
false,
|
|
143
|
+
),
|
|
144
|
+
retry: false,
|
|
145
|
+
note: 'unexpected shape',
|
|
146
|
+
};
|
|
147
|
+
return { ok: true, value: { absent: false, value: parsed.data } };
|
|
148
|
+
},
|
|
149
|
+
);
|
|
150
|
+
if (!read.ok) return read;
|
|
151
|
+
return read.value.absent
|
|
152
|
+
? { ok: true, absent: true }
|
|
153
|
+
: { ok: true, value: read.value.value };
|
|
79
154
|
}
|
|
@@ -60,7 +60,8 @@ export const ENTITY_STATUS_NOTE =
|
|
|
60
60
|
/** `entities` reads `ID_DESC`, and an entity id is a hash: the order says nothing about age. */
|
|
61
61
|
export const ENTITIES_ORDER = 'id (text)';
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
/** `text` cut to `max` characters, the last one an ellipsis when cut. */
|
|
64
|
+
export function clip(text: string, max: number): string {
|
|
64
65
|
return text.length > max ? `${text.slice(0, max - 1)}…` : text;
|
|
65
66
|
}
|
|
66
67
|
|
|
@@ -69,7 +70,7 @@ export function isRecord(value: unknown): value is Record<string, unknown> {
|
|
|
69
70
|
}
|
|
70
71
|
|
|
71
72
|
/** A non-empty string field, clipped; undefined otherwise. */
|
|
72
|
-
function text(value: unknown, max = TEXT_CHARS): string | undefined {
|
|
73
|
+
export function text(value: unknown, max = TEXT_CHARS): string | undefined {
|
|
73
74
|
return typeof value === 'string' && value ? clip(value, max) : undefined;
|
|
74
75
|
}
|
|
75
76
|
|