@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
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The `chain_lookup`
|
|
2
|
+
* The core `chain_lookup` kinds' arguments and the Blocksync root field
|
|
3
|
+
* each request becomes.
|
|
3
4
|
*
|
|
4
5
|
* Every field name, argument and enum below is checked against the
|
|
5
6
|
* ixo-blocksync-api schema (`public/graphql/schema.graphql`; see
|
|
@@ -15,13 +16,11 @@
|
|
|
15
16
|
import { IxoAddressSchema } from '@ixo/ixo-transaction';
|
|
16
17
|
import { z } from 'zod';
|
|
17
18
|
import { ixoAddressOf, type UserIdentity } from '../../core/user-identity';
|
|
18
|
-
import type { AliasedField } from './blocksync';
|
|
19
19
|
|
|
20
|
-
const MAX_REQUESTS = 8;
|
|
21
20
|
const MAX_FIRST = 20;
|
|
22
21
|
const DEFAULT_FIRST = 10;
|
|
23
22
|
/**
|
|
24
|
-
* Rows a
|
|
23
|
+
* Rows a call of several requests reads in all: each list request is
|
|
25
24
|
* capped at `max(5, floor(40 / requests))`.
|
|
26
25
|
*/
|
|
27
26
|
const SHARED_ROWS = 40;
|
|
@@ -40,99 +39,106 @@ const TX_HASH = /^[A-F0-9]{64}$/i;
|
|
|
40
39
|
const CURSOR = /^[A-Za-z0-9+/=_-]{1,200}$/;
|
|
41
40
|
const ENTITY_TYPE = /^[A-Za-z0-9/_.:-]{1,64}$/;
|
|
42
41
|
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
|
|
48
|
-
const
|
|
42
|
+
// These schemas are shown only through `describe_chain_kinds`, so each
|
|
43
|
+
// field says its format in full. Lengths are not capped here: every string
|
|
44
|
+
// is checked against its kind's bounded pattern before it reaches the
|
|
45
|
+
// document.
|
|
46
|
+
/** A list kind's `first` argument. */
|
|
47
|
+
export const firstArg = z
|
|
48
|
+
.number()
|
|
49
|
+
.int()
|
|
50
|
+
.min(1)
|
|
51
|
+
.max(MAX_FIRST)
|
|
52
|
+
.optional()
|
|
53
|
+
.describe(
|
|
54
|
+
'Rows to read, 1–20 (default 10; fewer when a call has several requests).',
|
|
55
|
+
);
|
|
56
|
+
/** An account argument (`role` names whose it is), the user's by default. */
|
|
57
|
+
export const accountArg = (role: string) =>
|
|
58
|
+
z
|
|
59
|
+
.string()
|
|
60
|
+
.optional()
|
|
61
|
+
.describe(
|
|
62
|
+
`${role}: an ixo1… address or did:ixo:ixo1… DID. Default: the user.`,
|
|
63
|
+
);
|
|
49
64
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
agent:
|
|
54
|
-
collectionId: z
|
|
55
|
-
|
|
56
|
-
|
|
65
|
+
/** Each core kind's arguments (strict: an unknown key refuses the request). */
|
|
66
|
+
export const CORE_ARGS = {
|
|
67
|
+
claims: z.strictObject({
|
|
68
|
+
agent: accountArg('Claim submitter'),
|
|
69
|
+
collectionId: z
|
|
70
|
+
.string()
|
|
71
|
+
.optional()
|
|
72
|
+
.describe('Only claims in this collection, e.g. "8250".'),
|
|
73
|
+
first: firstArg,
|
|
74
|
+
after: z
|
|
75
|
+
.string()
|
|
76
|
+
.optional()
|
|
77
|
+
.describe("The previous page's `endCursor`, for the next page."),
|
|
57
78
|
}),
|
|
58
|
-
z.strictObject({
|
|
59
|
-
kind: z.literal('claim'),
|
|
79
|
+
claim: z.strictObject({
|
|
60
80
|
claimId: z.string().describe('The claim id (a CID, e.g. bafkrei…).'),
|
|
61
81
|
}),
|
|
62
|
-
z.strictObject({
|
|
63
|
-
kind: z.literal('claim_collection'),
|
|
82
|
+
claim_collection: z.strictObject({
|
|
64
83
|
id: z.string().describe('Collection id, e.g. "8250".'),
|
|
65
84
|
}),
|
|
66
|
-
z.strictObject({
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
85
|
+
claim_collections: z.strictObject({
|
|
86
|
+
entity: z
|
|
87
|
+
.string()
|
|
88
|
+
.optional()
|
|
89
|
+
.describe(
|
|
90
|
+
"The collections' entity (did:ixo:entity:…); alone, every admin's.",
|
|
91
|
+
),
|
|
92
|
+
admin: accountArg('Collection admin'),
|
|
93
|
+
first: firstArg,
|
|
71
94
|
}),
|
|
72
|
-
z.strictObject({
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
first,
|
|
95
|
+
entities: z.strictObject({
|
|
96
|
+
owner: accountArg('Entity owner'),
|
|
97
|
+
type: z.string().optional().describe('Entity type, e.g. "dao".'),
|
|
98
|
+
first: firstArg,
|
|
77
99
|
}),
|
|
78
|
-
z.strictObject({
|
|
79
|
-
|
|
80
|
-
id: z.string().describe('did:ixo:entity:…'),
|
|
100
|
+
entity: z.strictObject({
|
|
101
|
+
id: z.string().describe('The entity DID: did:ixo:entity:<32 hex>.'),
|
|
81
102
|
}),
|
|
82
|
-
z.strictObject({
|
|
83
|
-
kind: z.literal('did_document'),
|
|
103
|
+
did_document: z.strictObject({
|
|
84
104
|
id: z
|
|
85
105
|
.string()
|
|
86
106
|
.optional()
|
|
87
|
-
.describe("Any did:ixo:… DID
|
|
107
|
+
.describe("Any did:ixo:… DID. Default: the user's."),
|
|
88
108
|
}),
|
|
89
|
-
z.strictObject({
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
first,
|
|
109
|
+
token_balances: z.strictObject({
|
|
110
|
+
address: accountArg('Token holder'),
|
|
111
|
+
first: firstArg,
|
|
93
112
|
}),
|
|
94
|
-
z.strictObject({
|
|
95
|
-
|
|
96
|
-
address: account('Signer or recipient.'),
|
|
113
|
+
transactions: z.strictObject({
|
|
114
|
+
address: accountArg('Signer or recipient'),
|
|
97
115
|
direction: z
|
|
98
116
|
.enum(['signed', 'received'])
|
|
99
117
|
.optional()
|
|
100
|
-
.describe('Default: signed.'),
|
|
101
|
-
first,
|
|
118
|
+
.describe('Signed by the address, or received by it. Default: signed.'),
|
|
119
|
+
first: firstArg,
|
|
102
120
|
}),
|
|
103
|
-
z.strictObject({
|
|
104
|
-
|
|
105
|
-
hash: z.string().describe('64-hex transaction hash.'),
|
|
121
|
+
transaction: z.strictObject({
|
|
122
|
+
hash: z.string().describe('The 64-hex transaction hash.'),
|
|
106
123
|
}),
|
|
107
|
-
|
|
124
|
+
};
|
|
108
125
|
|
|
109
|
-
|
|
110
|
-
|
|
126
|
+
type CoreArgs = typeof CORE_ARGS;
|
|
127
|
+
type ChainRequestKind = keyof CoreArgs;
|
|
128
|
+
/** A core kind's request: its name beside its parsed arguments. */
|
|
129
|
+
export type ChainRequest = {
|
|
130
|
+
[K in ChainRequestKind]: { kind: K } & z.output<CoreArgs[K]>;
|
|
131
|
+
}[ChainRequestKind];
|
|
111
132
|
export type Subject = 'user' | 'other';
|
|
112
133
|
|
|
113
|
-
export const chainLookupSchema = z.strictObject({
|
|
114
|
-
requests: z.array(chainRequestSchema).min(1).max(MAX_REQUESTS),
|
|
115
|
-
});
|
|
116
|
-
|
|
117
134
|
/**
|
|
118
|
-
* One request as a
|
|
119
|
-
*
|
|
120
|
-
* list request reads.
|
|
135
|
+
* One request as a root field of the lookup document (without its alias),
|
|
136
|
+
* or why it cannot be asked. `subject` is known before the read for a
|
|
137
|
+
* request by account; `first` is the rows a list request reads.
|
|
121
138
|
*/
|
|
122
139
|
export type FieldPlan =
|
|
123
|
-
|
|
|
124
|
-
|
|
125
|
-
request: ChainRequest;
|
|
126
|
-
kind: ChainRequestKind;
|
|
127
|
-
subject?: Subject;
|
|
128
|
-
first?: number;
|
|
129
|
-
} & AliasedField)
|
|
130
|
-
| {
|
|
131
|
-
ok: false;
|
|
132
|
-
request: ChainRequest;
|
|
133
|
-
kind: ChainRequestKind;
|
|
134
|
-
detail: string;
|
|
135
|
-
};
|
|
140
|
+
| { ok: true; field: string; subject?: Subject; first?: number }
|
|
141
|
+
| { ok: false; detail: string };
|
|
136
142
|
|
|
137
143
|
type Json = string | number | boolean | null | Json[] | { [key: string]: Json };
|
|
138
144
|
|
|
@@ -151,8 +157,8 @@ export function literal(value: Json): string {
|
|
|
151
157
|
}
|
|
152
158
|
|
|
153
159
|
/**
|
|
154
|
-
* The rows a list request reads: its `first` (1–20 by
|
|
155
|
-
* 10), capped at `max(5, floor(40 / count))` when the
|
|
160
|
+
* The rows a list request reads: its `first` (1–20 by its kind's schema,
|
|
161
|
+
* default 10), capped at `max(5, floor(40 / count))` when the call holds
|
|
156
162
|
* `count > 1` requests.
|
|
157
163
|
*/
|
|
158
164
|
export function firstOf(requested: number | undefined, count: number): number {
|
|
@@ -163,10 +169,61 @@ export function firstOf(requested: number | undefined, count: number): number {
|
|
|
163
169
|
return Math.min(requested ?? DEFAULT_FIRST, cap, MAX_FIRST);
|
|
164
170
|
}
|
|
165
171
|
|
|
172
|
+
const BECH32_CHARSET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l';
|
|
173
|
+
const BECH32_GENERATOR = [
|
|
174
|
+
0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3,
|
|
175
|
+
];
|
|
176
|
+
/** The checksum's length in characters. */
|
|
177
|
+
const BECH32_CHECKSUM = 6;
|
|
178
|
+
|
|
179
|
+
/** BIP-173 `bech32_polymod`. */
|
|
180
|
+
function polymod(values: readonly number[]): number {
|
|
181
|
+
let checksum = 1;
|
|
182
|
+
for (const value of values) {
|
|
183
|
+
const top = checksum >>> 25;
|
|
184
|
+
checksum = ((checksum & 0x1ffffff) << 5) ^ value;
|
|
185
|
+
BECH32_GENERATOR.forEach((generator, bit) => {
|
|
186
|
+
if ((top >>> bit) & 1) checksum ^= generator;
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
return checksum;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Whether `address` is a valid lower-case bech32 string (BIP-173, as Cosmos
|
|
194
|
+
* SDK addresses are) with the prefix `prefix`: only bech32 characters after
|
|
195
|
+
* `<prefix>1`, the checksum matching, and data that converts to whole
|
|
196
|
+
* bytes (at most 4 padding bits, all zero). Its length is the caller's to
|
|
197
|
+
* bound.
|
|
198
|
+
*/
|
|
199
|
+
export function isBech32(address: string, prefix: string): boolean {
|
|
200
|
+
if (!address.startsWith(`${prefix}1`)) return false;
|
|
201
|
+
const data = address.slice(prefix.length + 1);
|
|
202
|
+
if (data.length <= BECH32_CHECKSUM) return false;
|
|
203
|
+
const values: number[] = [];
|
|
204
|
+
for (const char of data) {
|
|
205
|
+
const value = BECH32_CHARSET.indexOf(char);
|
|
206
|
+
if (value < 0) return false;
|
|
207
|
+
values.push(value);
|
|
208
|
+
}
|
|
209
|
+
const codes = [...prefix].map((char) => char.charCodeAt(0));
|
|
210
|
+
const expanded = [
|
|
211
|
+
...codes.map((code) => code >> 5),
|
|
212
|
+
0,
|
|
213
|
+
...codes.map((code) => code & 31),
|
|
214
|
+
];
|
|
215
|
+
if (polymod([...expanded, ...values]) !== 1) return false;
|
|
216
|
+
const payload = values.length - BECH32_CHECKSUM;
|
|
217
|
+
const padding = (payload * 5) % 8;
|
|
218
|
+
const last = values[payload - 1] ?? 0;
|
|
219
|
+
return padding < 5 && (last & ((1 << padding) - 1)) === 0;
|
|
220
|
+
}
|
|
221
|
+
|
|
166
222
|
/**
|
|
167
223
|
* The account an account field names: the user's own address when the
|
|
168
224
|
* field is omitted, else an `ixo1…` address or the address a
|
|
169
|
-
* `did:ixo:ixo1…` DID names
|
|
225
|
+
* `did:ixo:ixo1…` DID names, its bech32 checksum verified (a mistyped
|
|
226
|
+
* address is refused, never read).
|
|
170
227
|
*/
|
|
171
228
|
export function accountOf(
|
|
172
229
|
input: string | undefined,
|
|
@@ -187,6 +244,11 @@ export function accountOf(
|
|
|
187
244
|
ok: false,
|
|
188
245
|
detail: `${role} must be an ixo1… address or a did:ixo:ixo1… DID.`,
|
|
189
246
|
};
|
|
247
|
+
if (!isBech32(address, 'ixo'))
|
|
248
|
+
return {
|
|
249
|
+
ok: false,
|
|
250
|
+
detail: `${role} is not a valid ixo1… address: its checksum does not match. Check it for a typo.`,
|
|
251
|
+
};
|
|
190
252
|
return {
|
|
191
253
|
ok: true,
|
|
192
254
|
address,
|
|
@@ -215,30 +277,17 @@ function equalities(pairs: [string, string | undefined][]): string {
|
|
|
215
277
|
);
|
|
216
278
|
}
|
|
217
279
|
|
|
280
|
+
const invalid = (detail: string): FieldPlan => ({ ok: false, detail });
|
|
281
|
+
|
|
218
282
|
/**
|
|
219
|
-
* One request as
|
|
220
|
-
*
|
|
283
|
+
* One request as a root field of the lookup document. `rows` is the rows a
|
|
284
|
+
* list request reads: its `first` as {@link firstOf} scales it for the call.
|
|
221
285
|
*/
|
|
222
286
|
export function requestField(
|
|
223
|
-
alias: string,
|
|
224
287
|
request: ChainRequest,
|
|
225
288
|
identity: UserIdentity,
|
|
226
|
-
|
|
289
|
+
rows: number,
|
|
227
290
|
): FieldPlan {
|
|
228
|
-
const plan = fieldOf(alias, request, identity, count);
|
|
229
|
-
return 'detail' in plan
|
|
230
|
-
? { ok: false, request, kind: request.kind, detail: plan.detail }
|
|
231
|
-
: { ok: true, request, kind: request.kind, alias, ...plan };
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
const invalid = (detail: string) => ({ detail });
|
|
235
|
-
|
|
236
|
-
function fieldOf(
|
|
237
|
-
alias: string,
|
|
238
|
-
request: ChainRequest,
|
|
239
|
-
identity: UserIdentity,
|
|
240
|
-
count: number,
|
|
241
|
-
): { field: string; subject?: Subject; first?: number } | { detail: string } {
|
|
242
291
|
switch (request.kind) {
|
|
243
292
|
case 'claims': {
|
|
244
293
|
const agent = accountOf(request.agent, identity, 'agent');
|
|
@@ -257,11 +306,11 @@ function fieldOf(
|
|
|
257
306
|
['collectionId', request.collectionId],
|
|
258
307
|
]);
|
|
259
308
|
const after = request.after ? `, after: ${literal(request.after)}` : '';
|
|
260
|
-
const n = firstOf(request.first, count);
|
|
261
309
|
return {
|
|
310
|
+
ok: true,
|
|
262
311
|
subject: agent.subject,
|
|
263
|
-
first:
|
|
264
|
-
field:
|
|
312
|
+
first: rows,
|
|
313
|
+
field: `claims(filter: ${filter}, first: ${rows}${after}, orderBy: SUBMISSION_DATE_DESC) { totalCount pageInfo { hasNextPage endCursor } nodes { ${CLAIM_FIELDS} evaluation { status evaluationDate } } }`,
|
|
265
314
|
};
|
|
266
315
|
}
|
|
267
316
|
case 'claim':
|
|
@@ -270,13 +319,15 @@ function fieldOf(
|
|
|
270
319
|
'claimId must be a claim id (a CID such as "bafkrei…").',
|
|
271
320
|
);
|
|
272
321
|
return {
|
|
273
|
-
|
|
322
|
+
ok: true,
|
|
323
|
+
field: `claim(claimId: ${literal(request.claimId)}) { ${CLAIM_FIELDS} evaluation { status evaluationDate oracle agentDid agentAddress reason amount } }`,
|
|
274
324
|
};
|
|
275
325
|
case 'claim_collection':
|
|
276
326
|
if (!COLLECTION_ID.test(request.id))
|
|
277
327
|
return invalid('id must be a collection number such as "8250".');
|
|
278
328
|
return {
|
|
279
|
-
|
|
329
|
+
ok: true,
|
|
330
|
+
field: `claimCollection(id: ${literal(request.id)}) { ${COLLECTION_FIELDS} payments }`,
|
|
280
331
|
};
|
|
281
332
|
case 'claim_collections': {
|
|
282
333
|
if (request.entity !== undefined && !ENTITY_DID.test(request.entity))
|
|
@@ -292,15 +343,15 @@ function fieldOf(
|
|
|
292
343
|
['entity', request.entity],
|
|
293
344
|
['admin', admin?.address],
|
|
294
345
|
]);
|
|
295
|
-
const n = firstOf(request.first, count);
|
|
296
346
|
// Newest start date first (the id is text: "9" sorts above "10"). A
|
|
297
347
|
// collection without a start date sorts first, as PostgreSQL puts
|
|
298
348
|
// NULLs first in a descending order; x/claims reads the start date on
|
|
299
349
|
// every claim submission, so a collection that takes claims has one.
|
|
300
350
|
return {
|
|
351
|
+
ok: true,
|
|
301
352
|
...(admin ? { subject: admin.subject } : {}),
|
|
302
|
-
first:
|
|
303
|
-
field:
|
|
353
|
+
first: rows,
|
|
354
|
+
field: `claimCollections(filter: ${filter}, first: ${rows}, orderBy: START_DATE_DESC) { totalCount nodes { ${COLLECTION_FIELDS} } }`,
|
|
304
355
|
};
|
|
305
356
|
}
|
|
306
357
|
case 'entities': {
|
|
@@ -312,18 +363,19 @@ function fieldOf(
|
|
|
312
363
|
['owner', owner.address],
|
|
313
364
|
['type', request.type],
|
|
314
365
|
]);
|
|
315
|
-
const n = firstOf(request.first, count);
|
|
316
366
|
return {
|
|
367
|
+
ok: true,
|
|
317
368
|
subject: owner.subject,
|
|
318
|
-
first:
|
|
319
|
-
field:
|
|
369
|
+
first: rows,
|
|
370
|
+
field: `entities(filter: ${filter}, first: ${rows}, orderBy: ID_DESC) { totalCount nodes { id type status owner metadata } }`,
|
|
320
371
|
};
|
|
321
372
|
}
|
|
322
373
|
case 'entity':
|
|
323
374
|
if (!ENTITY_DID.test(request.id))
|
|
324
375
|
return invalid('id must be a did:ixo:entity:… DID.');
|
|
325
376
|
return {
|
|
326
|
-
|
|
377
|
+
ok: true,
|
|
378
|
+
field: `entity(id: ${literal(request.id)}) { id type status owner accounts settings linkedResource linkedEntity controller service }`,
|
|
327
379
|
};
|
|
328
380
|
case 'did_document': {
|
|
329
381
|
const id = request.id ?? identity.did;
|
|
@@ -334,68 +386,48 @@ function fieldOf(
|
|
|
334
386
|
: 'id must be a did:ixo:… DID.',
|
|
335
387
|
);
|
|
336
388
|
return {
|
|
389
|
+
ok: true,
|
|
337
390
|
subject: id === identity.did ? 'user' : 'other',
|
|
338
|
-
field:
|
|
391
|
+
field: `iid(id: ${literal(id)}) { id controller verificationMethod linkedResource alsoKnownAs }`,
|
|
339
392
|
};
|
|
340
393
|
}
|
|
341
394
|
case 'token_balances': {
|
|
342
395
|
const holder = accountOf(request.address, identity, 'address');
|
|
343
396
|
if (!holder.ok) return invalid(holder.detail);
|
|
344
|
-
const n = firstOf(request.first, count);
|
|
345
397
|
return {
|
|
398
|
+
ok: true,
|
|
346
399
|
subject: holder.subject,
|
|
347
|
-
first:
|
|
348
|
-
field:
|
|
400
|
+
first: rows,
|
|
401
|
+
field: `tokenBalances(filter: ${equalities([['address', holder.address]])}, first: ${rows}, orderBy: AMOUNT_DESC) { totalCount nodes { tokenId amount retired token { name collection tokenClassByName { contractAddress class } } } }`,
|
|
349
402
|
};
|
|
350
403
|
}
|
|
351
404
|
case 'transactions': {
|
|
352
405
|
const holder = accountOf(request.address, identity, 'address');
|
|
353
406
|
if (!holder.ok) return invalid(holder.detail);
|
|
354
|
-
const n = firstOf(request.first, count);
|
|
355
407
|
if (request.direction === 'received')
|
|
356
408
|
// `Message.to` (indexed) is the recipient field of the message, or
|
|
357
409
|
// of its first inner message for an authz MsgExec: toAddress,
|
|
358
410
|
// receiver, recipient, recipientAddress or recipientDid. The same
|
|
359
411
|
// filter the Portal's wallet history polls with.
|
|
360
412
|
return {
|
|
413
|
+
ok: true,
|
|
361
414
|
subject: holder.subject,
|
|
362
|
-
first:
|
|
363
|
-
field:
|
|
415
|
+
first: rows,
|
|
416
|
+
field: `messages(filter: ${equalities([['to', holder.address]])}, first: ${rows}, orderBy: ID_DESC) { totalCount nodes { ${MESSAGE_FIELDS} transactionHash transactionByTransactionHash { ${TRANSACTION_FIELDS} } } }`,
|
|
364
417
|
};
|
|
365
418
|
return {
|
|
419
|
+
ok: true,
|
|
366
420
|
subject: holder.subject,
|
|
367
|
-
first:
|
|
368
|
-
field:
|
|
421
|
+
first: rows,
|
|
422
|
+
field: `transactionSigners(filter: ${equalities([['signerAddress', holder.address]])}, first: ${rows}, orderBy: TRANSACTION_ID_DESC) { totalCount nodes { transactionHash transaction { ${TRANSACTION_FIELDS} ${messagesOf(LIST_TX_MESSAGES)} } } }`,
|
|
369
423
|
};
|
|
370
424
|
}
|
|
371
425
|
case 'transaction':
|
|
372
426
|
if (!TX_HASH.test(request.hash))
|
|
373
427
|
return invalid('hash must be a 64-character hex transaction hash.');
|
|
374
428
|
return {
|
|
375
|
-
|
|
429
|
+
ok: true,
|
|
430
|
+
field: `transactions(filter: ${equalities([['hash', request.hash.toUpperCase()]])}, first: 1) { nodes { ${TRANSACTION_FIELDS} fee transactionSignersByTransactionId(first: ${TX_SIGNERS}) { nodes { signerAddress } } ${messagesOf(ONE_TX_MESSAGES)} } }`,
|
|
376
431
|
};
|
|
377
432
|
}
|
|
378
433
|
}
|
|
379
|
-
|
|
380
|
-
/** The alias of the request at `index`. */
|
|
381
|
-
const aliasOf = (index: number) => `r${index}`;
|
|
382
|
-
|
|
383
|
-
/**
|
|
384
|
-
* Every askable request as an aliased document field (aliases `r0…r7`, by
|
|
385
|
-
* position) and each request's plan. `fields` is empty when no request can
|
|
386
|
-
* be asked.
|
|
387
|
-
*/
|
|
388
|
-
export function buildDocument(
|
|
389
|
-
requests: readonly ChainRequest[],
|
|
390
|
-
identity: UserIdentity,
|
|
391
|
-
): { fields: AliasedField[]; plans: FieldPlan[] } {
|
|
392
|
-
const plans = requests.map((request, index) =>
|
|
393
|
-
requestField(aliasOf(index), request, identity, requests.length),
|
|
394
|
-
);
|
|
395
|
-
return {
|
|
396
|
-
fields: plans.flatMap((plan) =>
|
|
397
|
-
plan.ok ? [{ alias: plan.alias, field: plan.field }] : [],
|
|
398
|
-
),
|
|
399
|
-
plans,
|
|
400
|
-
};
|
|
401
|
-
}
|