@ixo/oracle-runtime-workers 0.29.1 → 0.31.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 +7 -5
- package/src/attachments/channel-links.ts +135 -0
- package/src/attachments/channel.ts +203 -0
- package/src/attachments/download.ts +23 -6
- package/src/attachments/index.ts +23 -0
- package/src/attachments/pipeline.ts +120 -42
- package/src/attachments/retention.ts +11 -3
- package/src/attachments/sanitize.ts +20 -8
- package/src/attachments/types.ts +7 -0
- package/src/attachments/view.ts +24 -7
- package/src/channels/contract.ts +69 -0
- package/src/channels/turns.ts +23 -1
- package/src/core/main-agent-types.ts +2 -0
- package/src/core/runtime-context.ts +3 -0
- package/src/delivery/plan.ts +3 -1
- package/src/do/contracts.ts +3 -0
- package/src/do/turn-attachments.ts +103 -0
- package/src/do/user-oracle-do.ts +142 -42
- package/src/hub-actions/client.ts +87 -7
- package/src/hub-actions/hub-text.ts +82 -0
- package/src/hub-actions/request.ts +7 -4
- package/src/hub-actions/store.ts +5 -1
- package/src/hub-actions/transfer.ts +23 -6
- package/src/index.ts +1 -0
- package/src/plugin-api/index.ts +1 -0
- package/src/plugin-api/types.ts +43 -2
- package/src/plugins/attachments/view-attachment-tool.ts +7 -3
- package/src/plugins/hub-actions/hub-actions.plugin.ts +96 -17
- package/src/plugins/ixo-chain/balances.ts +141 -0
- package/src/plugins/ixo-chain/blocksync.ts +17 -8
- package/src/plugins/ixo-chain/denoms.ts +84 -36
- package/src/plugins/ixo-chain/http.ts +315 -70
- 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 +135 -54
- package/src/plugins/ixo-chain/summaries.ts +3 -2
- package/src/plugins/yoma/catalog.ts +780 -0
- package/src/plugins/yoma/clef.ts +98 -0
- package/src/plugins/yoma/drafts.ts +339 -0
- package/src/plugins/yoma/engine.ts +926 -0
- package/src/plugins/yoma/index.ts +13 -0
- package/src/plugins/yoma/judge.ts +358 -0
- package/src/plugins/yoma/permission.ts +62 -0
- package/src/plugins/yoma/reads.ts +195 -0
- package/src/plugins/yoma/submission.ts +534 -0
- package/src/plugins/yoma/text.ts +16 -0
- package/src/plugins/yoma/work.ts +190 -0
- package/src/plugins/yoma/yoma.plugin.ts +2162 -0
- package/src/sqlite/serialization.ts +2 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An account's bank balances as `get_account_balances` shows them: one
|
|
3
|
+
* REST read of one page, each denom through its display, `uixo` first,
|
|
4
|
+
* then verified tokens, then by amount. The tool reads them with its own
|
|
5
|
+
* readers; `get_account_overview` reads them as an unregistered kind
|
|
6
|
+
* inside its one `runRequests` call.
|
|
7
|
+
*/
|
|
8
|
+
import { z } from 'zod';
|
|
9
|
+
import { formatUnits, type IbcTrace, type TokenDisplay } from './denoms';
|
|
10
|
+
import type { Outcome } from './http';
|
|
11
|
+
import { defineKind } from './kinds/types';
|
|
12
|
+
import { accountOf } from './queries';
|
|
13
|
+
|
|
14
|
+
/** Bank balances read per account (one page). */
|
|
15
|
+
export const BALANCE_PAGE = 50;
|
|
16
|
+
/** Tokens resolved at once; each runs its metadata, trace and channel reads in turn. */
|
|
17
|
+
const METADATA_CONCURRENCY = 4;
|
|
18
|
+
|
|
19
|
+
const balancesResponse = z.object({
|
|
20
|
+
balances: z.array(z.object({ denom: z.string(), amount: z.string() })),
|
|
21
|
+
pagination: z.object({ next_key: z.string().nullish() }).nullish(),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export type BalanceToken = {
|
|
25
|
+
denom: string;
|
|
26
|
+
symbol: string;
|
|
27
|
+
decimals: number;
|
|
28
|
+
verified: boolean;
|
|
29
|
+
amountBaseUnits: string;
|
|
30
|
+
amount: string;
|
|
31
|
+
origin?: string;
|
|
32
|
+
trace?: IbcTrace;
|
|
33
|
+
metadataUnavailable?: true;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export interface Balances {
|
|
37
|
+
tokens: BalanceToken[];
|
|
38
|
+
/** The account holds more denoms than one page. */
|
|
39
|
+
moreDenoms: boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** `items` mapped with at most `limit` running at once, in order. */
|
|
43
|
+
async function mapLimit<T, R>(
|
|
44
|
+
items: readonly T[],
|
|
45
|
+
limit: number,
|
|
46
|
+
map: (item: T) => Promise<R>,
|
|
47
|
+
): Promise<R[]> {
|
|
48
|
+
const out: R[] = [];
|
|
49
|
+
for (let index = 0; index < items.length; index += limit)
|
|
50
|
+
out.push(
|
|
51
|
+
...(await Promise.all(items.slice(index, index + limit).map(map))),
|
|
52
|
+
);
|
|
53
|
+
return out;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** How the balances are read: one REST GET and each denom's display. */
|
|
57
|
+
export interface BalanceReaders {
|
|
58
|
+
rest<S extends z.ZodType>(
|
|
59
|
+
path: string,
|
|
60
|
+
schema: S,
|
|
61
|
+
opts: { label: string },
|
|
62
|
+
): Promise<Outcome<z.infer<S>>>;
|
|
63
|
+
display(denom: string): Promise<TokenDisplay>;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* The bank balances of `address` (an `ixo1` address of bech32 characters,
|
|
68
|
+
* safe as a path segment), each token shown through `readers.display`.
|
|
69
|
+
*/
|
|
70
|
+
export async function readBalances(
|
|
71
|
+
readers: BalanceReaders,
|
|
72
|
+
address: string,
|
|
73
|
+
): Promise<Outcome<Balances>> {
|
|
74
|
+
const read = await readers.rest(
|
|
75
|
+
`/cosmos/bank/v1beta1/balances/${address}?pagination.limit=${BALANCE_PAGE}`,
|
|
76
|
+
balancesResponse,
|
|
77
|
+
{ label: 'balances' },
|
|
78
|
+
);
|
|
79
|
+
if (!read.ok) return read;
|
|
80
|
+
const coins = read.value.balances.filter((coin) =>
|
|
81
|
+
/^(0|[1-9][0-9]{0,39})$/.test(coin.amount),
|
|
82
|
+
);
|
|
83
|
+
const tokens: BalanceToken[] = await mapLimit(
|
|
84
|
+
coins,
|
|
85
|
+
METADATA_CONCURRENCY,
|
|
86
|
+
async (coin) => {
|
|
87
|
+
const display = await readers.display(coin.denom);
|
|
88
|
+
return {
|
|
89
|
+
denom: coin.denom,
|
|
90
|
+
symbol: display.symbol,
|
|
91
|
+
decimals: display.decimals,
|
|
92
|
+
verified: display.verified,
|
|
93
|
+
amountBaseUnits: coin.amount,
|
|
94
|
+
amount: formatUnits(coin.amount, display.decimals),
|
|
95
|
+
...(display.origin !== undefined ? { origin: display.origin } : {}),
|
|
96
|
+
...(display.trace !== undefined ? { trace: display.trace } : {}),
|
|
97
|
+
...(display.metadataUnavailable ? { metadataUnavailable: true } : {}),
|
|
98
|
+
};
|
|
99
|
+
},
|
|
100
|
+
);
|
|
101
|
+
const rank = (token: BalanceToken) =>
|
|
102
|
+
token.denom === 'uixo' ? 0 : token.verified ? 1 : 2;
|
|
103
|
+
tokens.sort(
|
|
104
|
+
(a, b) => rank(a) - rank(b) || Number(b.amount) - Number(a.amount),
|
|
105
|
+
);
|
|
106
|
+
return {
|
|
107
|
+
ok: true,
|
|
108
|
+
value: { tokens, moreDenoms: !!read.value.pagination?.next_key },
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* The balances as a kind, for `get_account_overview` only (it is not in
|
|
114
|
+
* the `chain_lookup` catalogue): `{ tokens, moreDenoms }` with the
|
|
115
|
+
* account's `subject`. Its denoms are looked up through the request's
|
|
116
|
+
* `display`, so at most `MAX_DISPLAY_DENOMS` of them.
|
|
117
|
+
*/
|
|
118
|
+
export const balancesKind = defineKind({
|
|
119
|
+
name: 'balances',
|
|
120
|
+
summary:
|
|
121
|
+
'The bank balances of an account, as get_account_balances reads them.',
|
|
122
|
+
explain:
|
|
123
|
+
'Each token: denom, symbol, decimals, verified, amountBaseUnits and `amount` in display units; uixo first, then verified tokens, then by amount.',
|
|
124
|
+
source: 'chain',
|
|
125
|
+
list: false,
|
|
126
|
+
args: z.strictObject({ address: z.string().optional() }),
|
|
127
|
+
example: {},
|
|
128
|
+
plan(input, ctx) {
|
|
129
|
+
const account = accountOf(input.address, ctx.identity, 'address');
|
|
130
|
+
if (!account.ok) return { invalid: account.detail };
|
|
131
|
+
return {
|
|
132
|
+
source: 'chain',
|
|
133
|
+
async run() {
|
|
134
|
+
const read = await readBalances(ctx, account.address);
|
|
135
|
+
return read.ok
|
|
136
|
+
? { ok: true, subject: account.subject, data: read.value }
|
|
137
|
+
: { ok: false, ...read.failure };
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
},
|
|
141
|
+
});
|
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
*
|
|
6
6
|
* PostGraphile answers field by field: one alias can be null with an error
|
|
7
7
|
* naming it while the others are fine, so errors are returned beside the
|
|
8
|
-
* data, never thrown.
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* would only time out again).
|
|
8
|
+
* data, never thrown. A transient database error ({@link TRANSIENT_ERROR}:
|
|
9
|
+
* an aborted transaction, a dropped connection, no free connection) says
|
|
10
|
+
* nothing about the question: the aliases it names are asked again once,
|
|
11
|
+
* in a document of only those aliases, and the answers merged; a
|
|
12
|
+
* statement timeout is never asked again (it would only time out again).
|
|
13
13
|
*/
|
|
14
14
|
import { z } from 'zod';
|
|
15
15
|
import {
|
|
@@ -28,8 +28,13 @@ export const BLOCKSYNC_MAX_BYTES = 1024 * 1024;
|
|
|
28
28
|
*/
|
|
29
29
|
export const BLOCKSYNC_ATTEMPT_TIMEOUT_MS = 11_000;
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Database errors that say nothing about the question: an aborted
|
|
33
|
+
* transaction, a dropped connection, and the server's connection pool or
|
|
34
|
+
* PostgreSQL itself out of connections.
|
|
35
|
+
*/
|
|
31
36
|
export const TRANSIENT_ERROR =
|
|
32
|
-
/current transaction is aborted|Connection terminated unexpectedly/;
|
|
37
|
+
/current transaction is aborted|Connection terminated unexpectedly|timeout exceeded when trying to connect|too many clients|remaining connection slots are reserved/;
|
|
33
38
|
export const STATEMENT_TIMEOUT = /statement timeout/i;
|
|
34
39
|
|
|
35
40
|
export interface GraphqlError {
|
|
@@ -187,13 +192,17 @@ async function post(
|
|
|
187
192
|
export function blocksyncLookup(
|
|
188
193
|
call: ChainCall,
|
|
189
194
|
fields: readonly AliasedField[],
|
|
190
|
-
opts: { timeoutMs?: number; maxBytes?: number } = {},
|
|
195
|
+
opts: { timeoutMs?: number; maxBytes?: number; tag?: string } = {},
|
|
191
196
|
): Promise<Outcome<BlocksyncAnswer>> {
|
|
192
197
|
let first: BlocksyncAnswer | null = null;
|
|
193
198
|
let retried: AliasedField[] = [];
|
|
194
199
|
return withRetry(
|
|
195
200
|
call,
|
|
196
|
-
{
|
|
201
|
+
{
|
|
202
|
+
label: 'blocksync',
|
|
203
|
+
upstream: call.blocksyncUrl,
|
|
204
|
+
...(opts.tag ? { tag: opts.tag } : {}),
|
|
205
|
+
},
|
|
197
206
|
async () => {
|
|
198
207
|
const asked = first ? retried : fields;
|
|
199
208
|
const read = await post(call, lookupDocument(asked), opts);
|
|
@@ -28,11 +28,17 @@
|
|
|
28
28
|
* as its symbol.
|
|
29
29
|
*
|
|
30
30
|
* A denom whose metadata could not be read (a transport failure, or no
|
|
31
|
-
* budget left to ask)
|
|
32
|
-
*
|
|
31
|
+
* budget left to ask) or was not asked (a `chain_lookup` request past its
|
|
32
|
+
* denom lookups) is shown the same way with `metadataUnavailable`: it may
|
|
33
|
+
* still be verified. Its trace is not read then.
|
|
33
34
|
*/
|
|
34
35
|
import { z } from 'zod';
|
|
35
|
-
import {
|
|
36
|
+
import {
|
|
37
|
+
hasBudget,
|
|
38
|
+
throwIfAborted,
|
|
39
|
+
untilAborted,
|
|
40
|
+
type ChainCall,
|
|
41
|
+
} from './http';
|
|
36
42
|
import { restJson } from './rest';
|
|
37
43
|
|
|
38
44
|
export type DenomMetadataView = { symbol: string; decimals: number } | null;
|
|
@@ -229,16 +235,26 @@ export const METADATA_NOT_FOUND_TTL_MS = 60_000;
|
|
|
229
235
|
export const METADATA_FAILED_TTL_MS = 10_000;
|
|
230
236
|
/** A denom trace or a channel's chain id never changes: found ones are reused for 1 h (misses and failures as for metadata). */
|
|
231
237
|
export const IBC_TRACE_TTL_MS = 3_600_000;
|
|
232
|
-
|
|
238
|
+
/** Answers kept per cache; past it the oldest written is dropped. */
|
|
239
|
+
export const DENOM_CACHE_LIMIT = 256;
|
|
233
240
|
|
|
234
|
-
/**
|
|
235
|
-
|
|
241
|
+
/**
|
|
242
|
+
* One read's answer: the value (null for a miss), or why there is none:
|
|
243
|
+
* `failed` (the upstream failed), `timeout` (it did not answer in time, or
|
|
244
|
+
* the launching call's budget ran out), `aborted` (the launching turn was
|
|
245
|
+
* aborted).
|
|
246
|
+
*/
|
|
247
|
+
type Lookup<T> = { value: T | null } | 'failed' | 'timeout' | 'aborted';
|
|
236
248
|
|
|
237
249
|
/**
|
|
238
250
|
* Answers per `${restUrl}|${key}`, kept per isolate: a found value for
|
|
239
|
-
* `foundTtlMs`, a miss for 60 s, a failure for 10 s, an abort
|
|
251
|
+
* `foundTtlMs`, a miss for 60 s, a failure for 10 s, a timeout or an abort
|
|
252
|
+
* never; at most {@link DENOM_CACHE_LIMIT}, the oldest dropped first. A
|
|
240
253
|
* read in flight is shared by every call of the isolate that needs the
|
|
241
|
-
* same key; none is started with less than 1 s of the budget left.
|
|
254
|
+
* same key; none is started with less than 1 s of the budget left. A call
|
|
255
|
+
* that joined a read which then timed out or was aborted by its launching
|
|
256
|
+
* turn reads again itself while its own budget allows; it waits on its own
|
|
257
|
+
* turn's abort, never another's.
|
|
242
258
|
*/
|
|
243
259
|
class ReadCache<T> {
|
|
244
260
|
private readonly answers = new Map<
|
|
@@ -249,18 +265,22 @@ class ReadCache<T> {
|
|
|
249
265
|
|
|
250
266
|
constructor(private readonly foundTtlMs: number) {}
|
|
251
267
|
|
|
252
|
-
get(
|
|
268
|
+
async get(
|
|
253
269
|
call: ChainCall,
|
|
254
270
|
key: string,
|
|
255
271
|
read: () => Promise<Lookup<T>>,
|
|
256
272
|
): Promise<Lookup<T>> {
|
|
257
273
|
const id = `${call.restUrl}|${key}`;
|
|
258
274
|
const hit = this.answers.get(id);
|
|
259
|
-
if (hit && hit.expiresAt > Date.now()) return
|
|
275
|
+
if (hit && hit.expiresAt > Date.now()) return hit.lookup;
|
|
260
276
|
const running = this.inflight.get(id);
|
|
261
|
-
if (running)
|
|
262
|
-
|
|
263
|
-
return
|
|
277
|
+
if (running) {
|
|
278
|
+
const lookup = await untilAborted(running, call.signal);
|
|
279
|
+
return (lookup === 'timeout' || lookup === 'aborted') && hasBudget(call)
|
|
280
|
+
? this.get(call, key, read)
|
|
281
|
+
: lookup;
|
|
282
|
+
}
|
|
283
|
+
if (!hasBudget(call)) return 'timeout';
|
|
264
284
|
const pending = read()
|
|
265
285
|
.then((lookup) => {
|
|
266
286
|
this.remember(id, lookup);
|
|
@@ -277,14 +297,18 @@ class ReadCache<T> {
|
|
|
277
297
|
}
|
|
278
298
|
|
|
279
299
|
private remember(id: string, lookup: Lookup<T>): void {
|
|
280
|
-
if (lookup === 'aborted') return;
|
|
300
|
+
if (lookup === 'aborted' || lookup === 'timeout') return;
|
|
281
301
|
const ttl =
|
|
282
302
|
lookup === 'failed'
|
|
283
303
|
? METADATA_FAILED_TTL_MS
|
|
284
304
|
: lookup.value
|
|
285
305
|
? this.foundTtlMs
|
|
286
306
|
: METADATA_NOT_FOUND_TTL_MS;
|
|
287
|
-
|
|
307
|
+
this.answers.delete(id);
|
|
308
|
+
if (this.answers.size >= DENOM_CACHE_LIMIT) {
|
|
309
|
+
const oldest = this.answers.keys().next();
|
|
310
|
+
if (!oldest.done) this.answers.delete(oldest.value);
|
|
311
|
+
}
|
|
288
312
|
this.answers.set(id, { lookup, expiresAt: Date.now() + ttl });
|
|
289
313
|
}
|
|
290
314
|
}
|
|
@@ -306,8 +330,9 @@ export function resetDenomMetadataCache(): void {
|
|
|
306
330
|
|
|
307
331
|
/**
|
|
308
332
|
* One bounded REST read of a resource that may be absent: a 404 is a
|
|
309
|
-
* miss, any other failure `failed`, the launching
|
|
310
|
-
* (so a call sharing the read is not failed by
|
|
333
|
+
* miss, a timeout `timeout`, any other failure `failed`, the launching
|
|
334
|
+
* turn's abort `aborted` (so a call sharing the read is not failed by
|
|
335
|
+
* another turn's abort).
|
|
311
336
|
*/
|
|
312
337
|
async function readRest<S extends z.ZodType, T>(
|
|
313
338
|
call: ChainCall,
|
|
@@ -328,7 +353,11 @@ async function readRest<S extends z.ZodType, T>(
|
|
|
328
353
|
throw error;
|
|
329
354
|
}
|
|
330
355
|
if (!read.ok)
|
|
331
|
-
return read.failure.reason === 'not_found'
|
|
356
|
+
return read.failure.reason === 'not_found'
|
|
357
|
+
? { value: null }
|
|
358
|
+
: read.failure.reason === 'timeout'
|
|
359
|
+
? 'timeout'
|
|
360
|
+
: 'failed';
|
|
332
361
|
return { value: pick(read.value) };
|
|
333
362
|
}
|
|
334
363
|
|
|
@@ -441,8 +470,7 @@ async function tracedDisplay(
|
|
|
441
470
|
const hash = denom.slice('ibc/'.length);
|
|
442
471
|
const trace = await traceCache.get(call, hash, () => readTrace(call, hash));
|
|
443
472
|
throwIfAborted(call.signal);
|
|
444
|
-
if (
|
|
445
|
-
return fallback;
|
|
473
|
+
if (typeof trace !== 'object' || !trace.value) return fallback;
|
|
446
474
|
const { baseDenom, path, channel } = trace.value;
|
|
447
475
|
const chainId = await chainIdCache.get(call, channel, () =>
|
|
448
476
|
readChainId(call, channel),
|
|
@@ -452,13 +480,43 @@ async function tracedDisplay(
|
|
|
452
480
|
...fallback,
|
|
453
481
|
trace: {
|
|
454
482
|
baseDenom,
|
|
455
|
-
claimedChain:
|
|
456
|
-
chainId === 'failed' || chainId === 'aborted' ? null : chainId.value,
|
|
483
|
+
claimedChain: typeof chainId === 'object' ? chainId.value : null,
|
|
457
484
|
path,
|
|
458
485
|
},
|
|
459
486
|
};
|
|
460
487
|
}
|
|
461
488
|
|
|
489
|
+
/**
|
|
490
|
+
* The display of `denom` when it takes no lookup: `uixo`, a denom that is
|
|
491
|
+
* not a bank denom (never looked up) and an `ibc/…` denom in `ibcDenoms`.
|
|
492
|
+
* Null for any other denom: its display needs its DenomMetadata.
|
|
493
|
+
*/
|
|
494
|
+
export function knownDisplay(
|
|
495
|
+
denom: string,
|
|
496
|
+
ibcDenoms: IbcDenomTable,
|
|
497
|
+
): TokenDisplay | null {
|
|
498
|
+
if (denom === 'uixo' || !DENOM.test(denom))
|
|
499
|
+
return trustedDenomDisplay(denom, null);
|
|
500
|
+
const known = IBC_DENOM.test(denom) ? ibcDenoms.get(denom) : undefined;
|
|
501
|
+
return known
|
|
502
|
+
? {
|
|
503
|
+
symbol: known.symbol,
|
|
504
|
+
decimals: known.decimals,
|
|
505
|
+
verified: true,
|
|
506
|
+
...(known.origin ? { origin: known.origin } : {}),
|
|
507
|
+
}
|
|
508
|
+
: null;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* A denom shown without its metadata (the read failed, or was not made):
|
|
513
|
+
* unverified, in base units, under its full denom, `metadataUnavailable`
|
|
514
|
+
* because it may still be verified.
|
|
515
|
+
*/
|
|
516
|
+
export function unreadDisplay(denom: string): TokenDisplay {
|
|
517
|
+
return { ...trustedDenomDisplay(denom, null), metadataUnavailable: true };
|
|
518
|
+
}
|
|
519
|
+
|
|
462
520
|
/**
|
|
463
521
|
* The display of `denom`: `uixo` and an `ibc/…` denom in `ibcDenoms`
|
|
464
522
|
* without a lookup; any other denom through its DenomMetadata, then (an
|
|
@@ -469,25 +527,15 @@ export async function denomDisplay(
|
|
|
469
527
|
denom: string,
|
|
470
528
|
ibcDenoms: IbcDenomTable,
|
|
471
529
|
): Promise<TokenDisplay> {
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
const ibc = IBC_DENOM.test(denom);
|
|
475
|
-
const known = ibc ? ibcDenoms.get(denom) : undefined;
|
|
476
|
-
if (known)
|
|
477
|
-
return {
|
|
478
|
-
symbol: known.symbol,
|
|
479
|
-
decimals: known.decimals,
|
|
480
|
-
verified: true,
|
|
481
|
-
...(known.origin ? { origin: known.origin } : {}),
|
|
482
|
-
};
|
|
530
|
+
const known = knownDisplay(denom, ibcDenoms);
|
|
531
|
+
if (known) return known;
|
|
483
532
|
const lookup = await metadataCache.get(call, denom, () =>
|
|
484
533
|
readMetadata(call, denom),
|
|
485
534
|
);
|
|
486
535
|
throwIfAborted(call.signal);
|
|
487
|
-
if (lookup
|
|
488
|
-
return { ...trustedDenomDisplay(denom, null), metadataUnavailable: true };
|
|
536
|
+
if (typeof lookup !== 'object') return unreadDisplay(denom);
|
|
489
537
|
const display = trustedDenomDisplay(denom, lookup.value);
|
|
490
|
-
return display.verified || !
|
|
538
|
+
return display.verified || !IBC_DENOM.test(denom)
|
|
491
539
|
? display
|
|
492
540
|
: tracedDisplay(call, denom, display);
|
|
493
541
|
}
|