@ouronet/talos-registry 1.4.0 → 2.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 CHANGED
@@ -178,10 +178,12 @@ import { tooltipModel, signatureRows } from "@ouronet/talos-registry";
178
178
  const m = tooltipModel("TS01-C1.DPTF|C_Transfer", { id: '"OURO-8Nh-JO8JO4F5"' });
179
179
  // m.slots -> EVERY execution parameter, in order, with type, value and flags
180
180
  // m.preview -> the INFO_ call with ITS OWN parameter list
181
- // m.kind -> "ouronet" | "native" (native renders gold, has no cost section)
181
+ // m.kind -> "ouronet" | "stoa" | "kadena" (only ouronet has a cost section)
182
+ // m.chainColor -> the CANONICAL border colour for that chain
183
+ // m.consumer -> who is rendering, for the caller zone
182
184
  ```
183
185
 
184
- Six rules, each there because it was got wrong first — most recently a six-parameter transfer
186
+ Eight rules, each there because it was got wrong first — most recently a six-parameter transfer
185
187
  whose tooltip showed five arguments, because it was rendering the preview's list under the
186
188
  execution's heading. Read the canon before building one.
187
189
 
package/TOOLTIP-CANON.md CHANGED
@@ -21,6 +21,19 @@ This file explains **why** each rule exists. Every one is here because it was go
21
21
 
22
22
  ---
23
23
 
24
+ ## Rule 0 — this tooltip is for CHAINWEB calls only
25
+
26
+ It describes a **Pact call on a Chainweb chain**: what function, what arguments, what it costs. A
27
+ button that transacts on **Arweave**, or any other non-Chainweb chain, has none of those things in
28
+ this shape.
29
+
30
+ **Such a button gets no tooltip of this kind — not an empty one.** An empty panel reads as "the
31
+ price failed to load"; absence reads as "this is a different kind of thing", which is the truth.
32
+ If you want an affordance there, write a different one; do not stretch this model over it.
33
+
34
+ `chainOf(key)` returns `undefined` for anything this model cannot describe, and `tooltipModels()`
35
+ drops such keys rather than throwing. That is how a caller learns to render nothing.
36
+
24
37
  ## Rule 1 — show every execution parameter
25
38
 
26
39
  All of them, always, in declared order.
@@ -74,19 +87,37 @@ already the widest thing on screen when it opens. A parallel row costs one line.
74
87
 
75
88
  `signatureRows(m)` returns `{ names, types }` as equal-length arrays for exactly this.
76
89
 
77
- ## Rule 4 — native is not Ouronet, and the difference is narrower than it looks
90
+ ## Rule 4 — three worlds, and they must be distinguishable
78
91
 
79
- A `coin.*` call is StoaChain's own root-namespace contract. It is **not** in this registry and
80
- never will be: the registry is generated from `ouronet-ns`.
92
+ `kind` is `"ouronet"`, `"stoa"` or `"kadena"`. Only the first is in this registry; the other two
93
+ are transcribed by hand in `STOA_SIGNATURES` and `KADENA_SIGNATURES`.
81
94
 
82
- What is actually different:
95
+ | | `ouronet` | `stoa` | `kadena` |
96
+ |---|---|---|---|
97
+ | what it is | the `ouronet-ns` surface | StoaChain's own `coin` | **Kadena mainnet's `coin`** |
98
+ | INFO_ preview | yes | **no** | **no** |
99
+ | IGNIS | collected | **none** | **none** |
100
+ | accounts | Ouronet glyph strings | `k:` `c:` `u:` `w:` | `k:` principals |
101
+ | gas | station pays | **station pays** | **a different chain's gas entirely** |
83
102
 
84
- | | Ouronet | native |
85
- |---|---|---|
86
- | INFO_ preview | yes | **no** — nothing prices a `coin` call |
87
- | IGNIS | collected | **none** |
88
- | accounts | Ouronet glyph strings | **Kadena-shaped** (`k:` `c:` `u:` `w:`) |
89
- | gas sponsorship | gas station pays | **gas station pays** — the same |
103
+ **`kadena` is not a variant of the other two.** Different chain, different gas, different balances;
104
+ nothing about Ouronet's gas station or IGNIS applies to it. Getting this wrong is worse than the
105
+ other confusions on this page, because a user who thinks a Kadena transfer is sponsored is wrong
106
+ about their own money.
107
+
108
+ **Both chains call their contract `coin`.** StoaChain uses Ouronet-style names (`C_Transfer`,
109
+ `C_URV|Stake`); Kadena uses KIP-0002 kebab-case (`transfer`, `transfer-create`). Nothing collides
110
+ today, and `chainOf` **throws** rather than guesses if anything ever does — picking wrong would
111
+ describe a call on the wrong chain, which is the kind of wrong that looks right.
112
+
113
+ Note `coin.transfer-crosschain` is a **`defpact`** — a multi-step continuation. Worth saying out
114
+ loud in a renderer: its later steps are not sponsored by anybody.
115
+
116
+ **The sponsorship row is the one to get right.** An earlier version of this claimed a StoaChain
117
+ native call was not sponsored and that the signing account paid its own gas. Both false: every
118
+ native modal carries `GAS_PAYER` on the gas-station key and says so in its own header comment. The
119
+ claim had been *inferred* from "this is not an Ouronet operation", which says nothing about who
120
+ pays the host chain — and it was wrong in the direction that matters.
90
121
 
91
122
  **That last row is the one to get right.** An earlier version of this claimed a native call was
92
123
  not sponsored and that the signing account paid its own STOA gas. Both false: every native modal
@@ -95,21 +126,61 @@ header comment. The claim had been *inferred* from "this is not an Ouronet opera
95
126
  nothing about who pays the host chain — and it was wrong in the direction that matters, telling a
96
127
  user they were about to spend when they were not.
97
128
 
98
- **Make the two visually distinct — the palette is yours.** The distinction is mandatory because
99
- the two differ in what they cost and what can be previewed; the specific colour is presentation,
100
- and this package does not get a vote on your design language. A rule that mandates a palette in
101
- someone else's application is a rule that gets ignored, and an ignored rule weakens the ones that
102
- matter.
129
+ ### The palette is canon — `CHAIN_PALETTE`
130
+
131
+ | chain | colour | |
132
+ |---|---|---|
133
+ | `ouronet` | `#3b82f6` | **blue** |
134
+ | `stoa` | `#ceac5f` | **gold** |
135
+ | `kadena` | `#22c55e` | **green** |
136
+
137
+ **This reverses the 1.4.0 position**, which said the palette was each app's business. The
138
+ reasoning then — that a package should not dictate colours inside someone else's design language —
139
+ was right about *decoration* and wrong about *this*. The colour encodes **which chain your money
140
+ is on**. Two apps teaching different meanings for the same colour is worse than neither using
141
+ colour at all, because a user believes whichever they learned first, and these two apps share
142
+ users.
103
143
 
104
- In practice:
144
+ `m.chainColor` and `m.chainLabel` are on the model so a renderer cannot quietly pick a different
145
+ one, and a test asserts every chain has both — if `Chain` ever gains a member without a colour,
146
+ that fails rather than rendering `undefined`, which CSS ignores and which therefore looks like
147
+ "no border rule" instead of a bug.
105
148
 
106
- | | native (`coin.*`) | Ouronet (`ouronet-ns.*`) |
149
+ Colours were taken from what was already in use rather than invented: gold is OuronetUI's STOA
150
+ accent, blue its established `#3b82f6`, green its success green — unused on any tooltip surface
151
+ and therefore free to carry this meaning.
152
+
153
+ **Make the distinction run in every direction.** We coloured `stoa` gold and left `ouronet` on the
154
+ panel's ordinary grey, so it only existed one way: a reader who never hovered a native button
155
+ learned no rule at all, and gold read as "something is odd about this one" rather than as a
156
+ category. Every kind gets a colour *and* a label.
157
+
158
+ And **say what is missing** rather than leaving the cost panel empty — an empty panel reads as
159
+ "the price failed to load".
160
+
161
+ ## Rule 4b — say who is rendering
162
+
163
+ Two applications draw this tooltip, and a Codex panel can open over an OuronetUI page. That is
164
+ exactly the case where "whose tooltip is this" is hardest to infer from chrome and most useful to
165
+ state.
166
+
167
+ **Carry a caller zone naming the consumer.** One line, its own accent:
168
+
169
+ ```ts
170
+ tooltipModel(key, values, CONSUMERS.Codex) // m.consumer -> { name, accent }
171
+ ```
172
+
173
+ | consumer | accent | |
107
174
  |---|---|---|
108
- | OuronetUI | gold `#ceac5f` | blue `#3b82f6` |
109
- | Codex | *its own* | violet |
175
+ | `OuronetUI` | `#d2d3d4` | neutral |
176
+ | `Codex` | `#8b5cf6` | **violet** |
177
+
178
+ **Two axes, deliberately.** The border says *what you are looking at*; the caller zone says *who
179
+ is showing it to you*. A consumer accent is therefore never a chain colour, and a test asserts the
180
+ two sets do not intersect — the moment they do, the two meanings merge and both become unreadable.
110
181
 
111
- Whatever you choose, make it carry meaning rather than decoration, and **say what is missing**
112
- rather than leaving the cost panel empty — an empty panel reads as "the price failed to load".
182
+ `name` matches the `consumerName` each app already uses for its codex settings, so the workspace
183
+ has one spelling of "OuronetUI" rather than two.
113
184
 
114
185
  Suggested footer, which is what OuronetUI ships:
115
186
 
@@ -118,8 +189,8 @@ no IGNIS — this is not an Ouronet operation
118
189
  a StoaChain `coin` call, so nothing prices it. Gas is still sponsored.
119
190
  ```
120
191
 
121
- Signatures for the native calls are in `NATIVE_SIGNATURES`, transcribed from the deployed
122
- contract with line numbers. Use them rather than typing your own.
192
+ Signatures are in `STOA_SIGNATURES` and `KADENA_SIGNATURES`, transcribed from the deployed
193
+ contracts with line numbers. Use them rather than typing your own.
123
194
 
124
195
  ## Rule 5 — a ghost is not data
125
196
 
package/dist/index.d.ts CHANGED
@@ -16,6 +16,6 @@ export type { CallPlan } from "./plan.js";
16
16
  export { parseCapability, parseCapabilities, capabilityRecipe } from "./caps.js";
17
17
  export type { ParsedCapability, CapabilityRecipe } from "./caps.js";
18
18
  export { formatForType, formatDecimal, formatInteger, formatString, formatBool } from "./format.js";
19
- export { tooltipModel, tooltipModels, signatureRows, isNativeKey, isPlaceholder, PLACEHOLDER, NATIVE_SIGNATURES } from "./tooltip.js";
20
- export type { TooltipModel, TooltipSlot, TooltipPreview } from "./tooltip.js";
19
+ export { tooltipModel, tooltipModels, signatureRows, chainOf, isOffRegistryKey, isNativeKey, isPlaceholder, PLACEHOLDER, CHAIN_PALETTE, CHAIN_LABEL, CONSUMERS, STOA_SIGNATURES, KADENA_SIGNATURES, NATIVE_SIGNATURES } from "./tooltip.js";
20
+ export type { TooltipModel, TooltipSlot, TooltipPreview, Chain, ConsumerIdentity } from "./tooltip.js";
21
21
  export type * from "./types.js";
package/dist/index.js CHANGED
@@ -15,4 +15,4 @@ export { parseCapability, parseCapabilities, capabilityRecipe } from "./caps.js"
15
15
  export { formatForType, formatDecimal, formatInteger, formatString, formatBool } from "./format.js";
16
16
  // THE TOOLTIP CANON. Shared implementation rather than a shared document -- see TOOLTIP-CANON.md
17
17
  // for why each rule exists, and `tooltip.ts` for the rules themselves.
18
- export { tooltipModel, tooltipModels, signatureRows, isNativeKey, isPlaceholder, PLACEHOLDER, NATIVE_SIGNATURES } from "./tooltip.js";
18
+ export { tooltipModel, tooltipModels, signatureRows, chainOf, isOffRegistryKey, isNativeKey, isPlaceholder, PLACEHOLDER, CHAIN_PALETTE, CHAIN_LABEL, CONSUMERS, STOA_SIGNATURES, KADENA_SIGNATURES, NATIVE_SIGNATURES } from "./tooltip.js";
package/dist/tooltip.d.ts CHANGED
@@ -1,3 +1,41 @@
1
+ /** The three worlds a tooltip can describe. See rule 4. */
2
+ export type Chain = "ouronet" | "stoa" | "kadena";
3
+ /**
4
+ * The canonical border colour per chain. USE THESE.
5
+ *
6
+ * Not styling: the colour tells a user which chain their money is on, and that vocabulary is
7
+ * shared between the applications that render it. Two apps teaching different meanings for the
8
+ * same colour is worse than neither using colour at all, because the user believes the first one
9
+ * they learned.
10
+ *
11
+ * ouronet blue the ouronet-ns surface -- priced, IGNIS, sponsored
12
+ * stoa gold StoaChain's own coin -- unpriced, no IGNIS, still sponsored
13
+ * kadena green a DIFFERENT CHAIN -- its own gas, its own balances
14
+ *
15
+ * Chosen from what was already in use rather than invented: gold is OuronetUI's STOA accent and
16
+ * blue its established `#3b82f6`; green is the app's existing success green, which is unused on
17
+ * any other tooltip surface and therefore free to carry this meaning.
18
+ */
19
+ export declare const CHAIN_PALETTE: Readonly<Record<Chain, string>>;
20
+ /** Human label for the chain, for the border-adjacent marker. */
21
+ export declare const CHAIN_LABEL: Readonly<Record<Chain, string>>;
22
+ /**
23
+ * Who is drawing the tooltip -- rule 4b.
24
+ *
25
+ * Two applications render this, and a Codex panel can open over an OuronetUI page, which is
26
+ * exactly the case where "whose tooltip is this" is hardest to infer from chrome and most useful
27
+ * to state. The accent is the CONSUMER's identity and is deliberately a different axis from
28
+ * `CHAIN_PALETTE`: one says what you are looking at, the other says who is showing it to you.
29
+ *
30
+ * `name` matches the `consumerName` key each app already uses for its codex settings, so there is
31
+ * one spelling of "OuronetUI" across the workspace rather than two.
32
+ */
33
+ export interface ConsumerIdentity {
34
+ name: string;
35
+ /** the app's own accent for the caller zone. NOT a chain colour. */
36
+ accent: string;
37
+ }
38
+ export declare const CONSUMERS: Readonly<Record<"OuronetUI" | "Codex", ConsumerIdentity>>;
1
39
  /** One parameter of the execution, with everything a renderer needs about it. */
2
40
  export interface TooltipSlot {
3
41
  /** 1-based, so a renderer never has to decide whether to add one. */
@@ -28,13 +66,19 @@ export interface TooltipModel {
28
66
  /**
29
67
  * Which world this call belongs to -- rule 4.
30
68
  *
31
- * A renderer MUST make the two visually distinct, because they differ in what they cost and
32
- * what can be previewed. It is NOT told how: OuronetUI uses gold for native and blue for
33
- * Ouronet, the Codex uses violet, and both are conformant. Mandating a palette in a package
34
- * consumed by applications with their own design languages would be the kind of rule that gets
35
- * ignored, and a rule that gets ignored weakens the ones that matter.
69
+ * ouronet the `ouronet-ns` surface. Priced by an INFO_ reader, collects IGNIS.
70
+ * stoa StoaChain's own root-namespace `coin`. No preview, no IGNIS, gas still sponsored.
71
+ * kadena Kadena mainnet's `coin`. A DIFFERENT CHAIN -- different gas, different accounts,
72
+ * and nothing about Ouronet applies to it.
73
+ *
74
+ * Each has a canonical colour in `CHAIN_PALETTE`, and a renderer should use it: the colour
75
+ * encodes which chain the user's money is on, which is shared vocabulary rather than styling.
76
+ *
77
+ * RENAMED from `"native"` in 2.0.0. With only two worlds "native" was unambiguous; with three
78
+ * it silently invited the question "native to WHAT" -- and both candidate answers are a
79
+ * contract called `coin`.
36
80
  */
37
- kind: "ouronet" | "native";
81
+ kind: "ouronet" | "stoa" | "kadena";
38
82
  /** fully qualified execution, as it would be typed in a transaction. */
39
83
  exec: string;
40
84
  /** EVERY execution parameter, in declared order -- rule 1. */
@@ -48,24 +92,54 @@ export interface TooltipModel {
48
92
  * latency the user pays for a foregone conclusion. Rule 5.
49
93
  */
50
94
  shouldRead: boolean;
95
+ /** the canonical border colour for `kind`. Convenience -- identical to CHAIN_PALETTE[kind]. */
96
+ chainColor: string;
97
+ /** the label to print beside it: OURONET / STOA NATIVE / KADENA. */
98
+ chainLabel: string;
99
+ /** who is rendering, for the caller zone. Undefined when the caller did not say. */
100
+ consumer?: ConsumerIdentity;
51
101
  /** things a renderer may want to surface. Never thrown; a tooltip must not take a page down. */
52
102
  warnings: string[];
53
103
  }
54
104
  /** The registry's generic placeholder: an entity id for something that does not exist on chain. */
55
105
  export declare const PLACEHOLDER = "\"example\"";
56
106
  export declare const isPlaceholder: (rendered: string) => boolean;
57
- /** A `coin.*` key is StoaChain's root-namespace contract -- outside this registry by design. */
58
- export declare const isNativeKey: (key: string) => boolean;
59
107
  /**
60
- * STOA-native specs, which the generated registry does not and will not describe.
108
+ * Off-registry signatures, by chain.
109
+ *
110
+ * Neither chain's `coin` contract is in the generated registry -- that describes `ouronet-ns`
111
+ * only -- so these are transcribed by hand with their source lines. They live HERE rather than in
112
+ * each consumer so both applications render the same signatures instead of each keeping a copy.
113
+ *
114
+ * BOTH CHAINS CALL THEIR CONTRACT `coin`, which is the one real hazard in this table. StoaChain's
115
+ * uses Ouronet-style names (`C_Transfer`, `C_URV|Stake`); Kadena's uses KIP-0002 kebab-case
116
+ * (`transfer`, `transfer-create`). No key collides TODAY, and `chainOf` refuses rather than
117
+ * guesses if one ever does -- the same posture `resolveByName` takes for the four SWP names that
118
+ * resolve to two modules. Guessing wrong here would price a call on the wrong chain.
119
+ */
120
+ /** StoaChain's root-namespace `coin`. source: 0_Stoa/coin-contract/coin-live.pact */
121
+ export declare const STOA_SIGNATURES: Readonly<Record<string, ReadonlyArray<[string, string]>>>;
122
+ /**
123
+ * Kadena mainnet's `coin`. source: 00_KadenaSandbox/kda-env/kadena/coin-v6.pact
61
124
  *
62
- * They live HERE rather than in each consumer so that both applications render the same
63
- * signatures. Transcribed from the deployed contract with its line numbers; `tooltip.test.ts`
64
- * asserts internal consistency and the Pact repo's own test re-reads the source.
125
+ * A DIFFERENT CHAIN. Different gas, different accounts, and nothing about Ouronet's gas station
126
+ * or IGNIS applies. `transfer-crosschain` is a `defpact` -- a multi-step continuation -- which a
127
+ * renderer may want to say out loud, because its later steps are not sponsored by anyone.
128
+ */
129
+ export declare const KADENA_SIGNATURES: Readonly<Record<string, ReadonlyArray<[string, string]>>>;
130
+ /** Back-compat alias. `NATIVE_SIGNATURES` meant StoaChain's before Kadena joined the model. */
131
+ export declare const NATIVE_SIGNATURES: Readonly<Record<string, readonly [string, string][]>>;
132
+ /**
133
+ * Which chain a key belongs to, or `undefined` for a key this model does not describe.
65
134
  *
66
- * source: 0_Stoa/coin-contract/coin-live.pact
135
+ * Refuses an AMBIGUOUS key rather than picking: both chains have a `coin`, and pricing a call on
136
+ * the wrong chain is the kind of wrong that looks right.
67
137
  */
68
- export declare const NATIVE_SIGNATURES: Readonly<Record<string, ReadonlyArray<[string, string]>>>;
138
+ export declare function chainOf(key: string): "ouronet" | "stoa" | "kadena" | undefined;
139
+ /** True for any key this model can describe at all. Arweave and friends answer false -- rule 0. */
140
+ export declare const isOffRegistryKey: (key: string) => boolean;
141
+ /** @deprecated use `chainOf(key) === "stoa"`. Kept so 1.x callers keep compiling. */
142
+ export declare const isNativeKey: (key: string) => boolean;
69
143
  /**
70
144
  * Everything a renderer needs for one entrypoint.
71
145
  *
@@ -77,7 +151,7 @@ export declare const NATIVE_SIGNATURES: Readonly<Record<string, ReadonlyArray<[s
77
151
  * unresolvable argument becomes a visible placeholder rather than an exception, because a tooltip
78
152
  * must never take down the page it annotates.
79
153
  */
80
- export declare function tooltipModel(key: string, values?: Readonly<Record<string, string>>): TooltipModel;
154
+ export declare function tooltipModel(key: string, values?: Readonly<Record<string, string>>, consumer?: ConsumerIdentity): TooltipModel;
81
155
  /**
82
156
  * Models for a button that fronts a CHOICE of executions, in the order the ZBOM presents them.
83
157
  *
@@ -86,7 +160,7 @@ export declare function tooltipModel(key: string, values?: Readonly<Record<strin
86
160
  * caller instead -- and if nothing survives, the caller renders no tooltip, which is the correct
87
161
  * outcome and not a crash.
88
162
  */
89
- export declare function tooltipModels(keys: readonly string[], values?: Readonly<Record<string, string>>): TooltipModel[];
163
+ export declare function tooltipModels(keys: readonly string[], values?: Readonly<Record<string, string>>, consumer?: ConsumerIdentity): TooltipModel[];
90
164
  /**
91
165
  * The type row -- rule 3.
92
166
  *
package/dist/tooltip.js CHANGED
@@ -22,10 +22,28 @@
22
22
  * 3. TYPES GO ON THEIR OWN ROW. `patron:string executor:string ...` doubles the width of the
23
23
  * widest line in the panel. A parallel row under the names costs one line and reads better.
24
24
  *
25
- * 4. NATIVE IS NOT OURONET, and the difference is narrow. A `coin.*` call has no INFO_ preview
26
- * and collects no IGNIS. Gas IS still sponsored -- claiming otherwise tells a user they are
27
- * about to spend when they are not. The two MUST be distinguishable at a glance; HOW is the
28
- * implementation's choice. `kind` is the canon; the palette is not.
25
+ * 4. THREE WORLDS, WITH A CANONICAL COLOUR EACH. `ouronet` (blue), `stoa` (gold), `kadena`
26
+ * (green). They differ in what they cost and what can be previewed. Only `ouronet` has an
27
+ * INFO_ reader and collects IGNIS; gas is sponsored on the Stoa side regardless -- claiming
28
+ * otherwise tells a user they are about to spend when they are not.
29
+ *
30
+ * THE PALETTE IS NOW CANON, reversing the 1.4.0 position that it was each app's business.
31
+ * The argument then was that a package should not dictate colours inside somebody else's
32
+ * design language, and that was right about DECORATION and wrong about this: the colour
33
+ * encodes WHICH CHAIN YOUR MONEY IS ON. A user who learns green-means-Kadena in one app and
34
+ * meets green-means-something-else in the other has been taught a falsehood by the
35
+ * inconsistency, and the two apps share users. A shared vocabulary has to be shared.
36
+ *
37
+ * 4b. SAY WHO IS RENDERING. Two applications draw this tooltip, and they draw it over different
38
+ * surfaces for the same chains. A caller zone naming the consumer costs one line and answers
39
+ * "which app am I looking at" without the user having to infer it from chrome -- which matters
40
+ * most in exactly the case where it is hardest, a Codex panel over an OuronetUI page.
41
+ *
42
+ * 0. CHAINWEB ONLY -- the rule that decides whether a tooltip exists at all. This model describes
43
+ * a PACT call on a Chainweb chain: what function, what arguments, what it costs. A button that
44
+ * transacts on Arweave, or any non-Chainweb chain, has none of those things in this shape and
45
+ * gets NO tooltip of this kind rather than an empty one. An empty panel reads as a failure to
46
+ * load; absence reads as "this is a different kind of thing", which is the truth.
29
47
  *
30
48
  * 5. A GHOST IS NOT DATA. An unresolved argument must LOOK unresolved, and a tooltip whose
31
49
  * arguments are placeholders must not fire the preview read at all: the answer is a foregone
@@ -33,22 +51,55 @@
33
51
  */
34
52
  import { getEntrypoint, getPreview, tryGetEntrypoint, registry } from "./registry.js";
35
53
  import { formatForType } from "./format.js";
54
+ /**
55
+ * The canonical border colour per chain. USE THESE.
56
+ *
57
+ * Not styling: the colour tells a user which chain their money is on, and that vocabulary is
58
+ * shared between the applications that render it. Two apps teaching different meanings for the
59
+ * same colour is worse than neither using colour at all, because the user believes the first one
60
+ * they learned.
61
+ *
62
+ * ouronet blue the ouronet-ns surface -- priced, IGNIS, sponsored
63
+ * stoa gold StoaChain's own coin -- unpriced, no IGNIS, still sponsored
64
+ * kadena green a DIFFERENT CHAIN -- its own gas, its own balances
65
+ *
66
+ * Chosen from what was already in use rather than invented: gold is OuronetUI's STOA accent and
67
+ * blue its established `#3b82f6`; green is the app's existing success green, which is unused on
68
+ * any other tooltip surface and therefore free to carry this meaning.
69
+ */
70
+ export const CHAIN_PALETTE = {
71
+ ouronet: "#3b82f6",
72
+ stoa: "#ceac5f",
73
+ kadena: "#22c55e",
74
+ };
75
+ /** Human label for the chain, for the border-adjacent marker. */
76
+ export const CHAIN_LABEL = {
77
+ ouronet: "OURONET",
78
+ stoa: "STOA NATIVE",
79
+ kadena: "KADENA",
80
+ };
81
+ export const CONSUMERS = {
82
+ OuronetUI: { name: "OuronetUI", accent: "#d2d3d4" },
83
+ Codex: { name: "Codex", accent: "#8b5cf6" },
84
+ };
36
85
  /** The registry's generic placeholder: an entity id for something that does not exist on chain. */
37
86
  export const PLACEHOLDER = '"example"';
38
87
  export const isPlaceholder = (rendered) => rendered === PLACEHOLDER;
39
- /** A `coin.*` key is StoaChain's root-namespace contract -- outside this registry by design. */
40
- export const isNativeKey = (key) => key.startsWith("coin.");
41
88
  /**
42
- * STOA-native specs, which the generated registry does not and will not describe.
89
+ * Off-registry signatures, by chain.
43
90
  *
44
- * They live HERE rather than in each consumer so that both applications render the same
45
- * signatures. Transcribed from the deployed contract with its line numbers; `tooltip.test.ts`
46
- * asserts internal consistency and the Pact repo's own test re-reads the source.
91
+ * Neither chain's `coin` contract is in the generated registry -- that describes `ouronet-ns`
92
+ * only -- so these are transcribed by hand with their source lines. They live HERE rather than in
93
+ * each consumer so both applications render the same signatures instead of each keeping a copy.
47
94
  *
48
- * source: 0_Stoa/coin-contract/coin-live.pact
95
+ * BOTH CHAINS CALL THEIR CONTRACT `coin`, which is the one real hazard in this table. StoaChain's
96
+ * uses Ouronet-style names (`C_Transfer`, `C_URV|Stake`); Kadena's uses KIP-0002 kebab-case
97
+ * (`transfer`, `transfer-create`). No key collides TODAY, and `chainOf` refuses rather than
98
+ * guesses if one ever does -- the same posture `resolveByName` takes for the four SWP names that
99
+ * resolve to two modules. Guessing wrong here would price a call on the wrong chain.
49
100
  */
50
- export const NATIVE_SIGNATURES = {
51
- // [name, type] pairs, in declared order.
101
+ /** StoaChain's root-namespace `coin`. source: 0_Stoa/coin-contract/coin-live.pact */
102
+ export const STOA_SIGNATURES = {
52
103
  "coin.C_Transfer": [["sender", "string"], ["receiver", "string"], ["amount", "decimal"]], // :583
53
104
  "coin.C_Transmit": [["sender", "string"], ["receiver", "string"], ["amount", "decimal"]], // :593
54
105
  "coin.C_TransferAnew": [["sender", "string"], ["receiver", "string"], ["receiver-guard", "guard"], ["amount", "decimal"]], // :586
@@ -59,37 +110,85 @@ export const NATIVE_SIGNATURES = {
59
110
  "coin.C_URV|Unstake": [["account", "string"], ["urstoa-amount", "decimal"]], // :1439
60
111
  "coin.C_URV|Collect": [["account", "string"]], // :1467
61
112
  };
62
- /** Kadena-shaped, NOT an Ouronet glyph account -- a `coin` call takes k:/c:/u:/w:. */
63
- const NATIVE_GHOST = {
113
+ /**
114
+ * Kadena mainnet's `coin`. source: 00_KadenaSandbox/kda-env/kadena/coin-v6.pact
115
+ *
116
+ * A DIFFERENT CHAIN. Different gas, different accounts, and nothing about Ouronet's gas station
117
+ * or IGNIS applies. `transfer-crosschain` is a `defpact` -- a multi-step continuation -- which a
118
+ * renderer may want to say out loud, because its later steps are not sponsored by anyone.
119
+ */
120
+ export const KADENA_SIGNATURES = {
121
+ "coin.transfer": [["sender", "string"], ["receiver", "string"], ["amount", "decimal"]], // :329
122
+ "coin.transfer-create": [["sender", "string"], ["receiver", "string"], ["receiver-guard", "guard"], ["amount", "decimal"]], // :356
123
+ "coin.transfer-crosschain": [["sender", "string"], ["receiver", "string"], ["receiver-guard", "guard"], ["target-chain", "string"], ["amount", "decimal"]], // :528 (defpact)
124
+ "coin.create-account": [["account", "string"], ["guard", "guard"]], // :278
125
+ "coin.rotate": [["account", "string"], ["new-guard", "guard"]], // :307
126
+ };
127
+ /** Back-compat alias. `NATIVE_SIGNATURES` meant StoaChain's before Kadena joined the model. */
128
+ export const NATIVE_SIGNATURES = STOA_SIGNATURES;
129
+ /**
130
+ * Which chain a key belongs to, or `undefined` for a key this model does not describe.
131
+ *
132
+ * Refuses an AMBIGUOUS key rather than picking: both chains have a `coin`, and pricing a call on
133
+ * the wrong chain is the kind of wrong that looks right.
134
+ */
135
+ export function chainOf(key) {
136
+ const inStoa = key in STOA_SIGNATURES;
137
+ const inKda = key in KADENA_SIGNATURES;
138
+ if (inStoa && inKda)
139
+ throw new Error(`tooltip: "${key}" is declared for BOTH StoaChain and Kadena. Both chains have a \`coin\` ` +
140
+ `contract, so this cannot be resolved by name -- and choosing wrong would describe a call ` +
141
+ `on the wrong chain. Remove the duplicate or key it explicitly.`);
142
+ if (inStoa)
143
+ return "stoa";
144
+ if (inKda)
145
+ return "kadena";
146
+ return tryGetEntrypoint(key) ? "ouronet" : undefined;
147
+ }
148
+ /** True for any key this model can describe at all. Arweave and friends answer false -- rule 0. */
149
+ export const isOffRegistryKey = (key) => key in STOA_SIGNATURES || key in KADENA_SIGNATURES;
150
+ /** @deprecated use `chainOf(key) === "stoa"`. Kept so 1.x callers keep compiling. */
151
+ export const isNativeKey = (key) => key in STOA_SIGNATURES;
152
+ /** StoaChain `coin` accounts are k:/c:/u:/w: shaped, NOT Ouronet glyph strings. */
153
+ const STOA_GHOST = {
64
154
  string: '"k:1ac0d8b0a4f6e2c9d3b5a7e1f4c6089d2b3e5a7c9f1d3b5e7a9c1f3d5b7e9a1c"',
65
155
  decimal: "1.0",
66
156
  integer: "1",
67
157
  bool: "false",
68
158
  guard: '(read-keyset "ks")',
69
159
  };
70
- function nativeModel(key, values) {
71
- // Callers reach this only through `tooltipModel`, which has already rejected an unknown key.
72
- // Asserted rather than assumed: a non-null assertion here would make a future direct caller
73
- // fail with a property access on undefined instead of a sentence naming the problem.
74
- const sig = NATIVE_SIGNATURES[key];
160
+ /** Kadena accounts are `k:` principals. Same shape, different chain -- and a different balance. */
161
+ const KADENA_GHOST = {
162
+ string: '"k:1ac0d8b0a4f6e2c9d3b5a7e1f4c6089d2b3e5a7c9f1d3b5e7a9c1f3d5b7e9a1c"',
163
+ decimal: "1.0",
164
+ integer: "1",
165
+ bool: "false",
166
+ guard: '(read-keyset "ks")',
167
+ };
168
+ function offRegistryModel(key, kind, values, consumer) {
169
+ const sig = (kind === "stoa" ? STOA_SIGNATURES : KADENA_SIGNATURES)[key];
75
170
  if (!sig)
76
- throw new Error(`nativeModel: no signature for "${key}"`);
171
+ throw new Error(`offRegistryModel: no signature for "${key}"`);
172
+ const ghost = kind === "stoa" ? STOA_GHOST : KADENA_GHOST;
77
173
  return {
78
- kind: "native",
174
+ kind,
79
175
  exec: key,
176
+ chainColor: CHAIN_PALETTE[kind],
177
+ chainLabel: CHAIN_LABEL[kind],
178
+ consumer,
80
179
  slots: sig.map(([name, type], i) => ({
81
180
  index: i + 1,
82
181
  name,
83
182
  type,
84
- value: values[name] ?? NATIVE_GHOST[type] ?? PLACEHOLDER,
85
- isPlaceholder: values[name] === undefined && NATIVE_GHOST[type] === undefined,
183
+ value: values[name] ?? ghost[type] ?? PLACEHOLDER,
184
+ isPlaceholder: values[name] === undefined && ghost[type] === undefined,
86
185
  isPreflightFed: false,
87
- // A guard is never prefillable, native or not. Same rule, same reason.
186
+ // A guard is never prefillable, on any chain. Same rule, same reason.
88
187
  prefillable: type !== "guard",
89
188
  })),
90
- // No preview, deliberately. Rule 4: nothing on `coin` prices a call, so there is no cost to
91
- // show and a renderer must say that rather than leaving an empty panel that reads as a
92
- // failed load.
189
+ // No preview, deliberately. Neither chain's `coin` has a reader that prices a call, so there
190
+ // is no cost to show -- and a renderer must SAY that rather than leaving an empty panel,
191
+ // which reads as "the price failed to load".
93
192
  shouldRead: false,
94
193
  warnings: [],
95
194
  };
@@ -105,13 +204,12 @@ function nativeModel(key, values) {
105
204
  * unresolvable argument becomes a visible placeholder rather than an exception, because a tooltip
106
205
  * must never take down the page it annotates.
107
206
  */
108
- export function tooltipModel(key, values = {}) {
109
- if (isNativeKey(key)) {
110
- if (!NATIVE_SIGNATURES[key])
111
- throw new Error(`tooltipModel: unknown native key "${key}". ` +
112
- `Known: ${Object.keys(NATIVE_SIGNATURES).join(", ")}`);
113
- return nativeModel(key, values);
114
- }
207
+ export function tooltipModel(key, values = {}, consumer) {
208
+ const chain = chainOf(key);
209
+ if (chain === "stoa" || chain === "kadena")
210
+ return offRegistryModel(key, chain, values, consumer);
211
+ if (chain === undefined && isOffRegistryKey(key))
212
+ throw new Error(`tooltipModel: "${key}" matched no chain. This is a bug in chainOf.`);
115
213
  const ep = getEntrypoint(key); // throws, and names near matches, on a stale key
116
214
  const warnings = [];
117
215
  const use = ep.ghost.use ?? {};
@@ -195,6 +293,9 @@ export function tooltipModel(key, values = {}) {
195
293
  return {
196
294
  kind: "ouronet",
197
295
  exec: `${registry.namespace}.${key}`,
296
+ chainColor: CHAIN_PALETTE.ouronet,
297
+ chainLabel: CHAIN_LABEL.ouronet,
298
+ consumer,
198
299
  slots,
199
300
  preview,
200
301
  // RULE 5: do not fire a read whose answer is a foregone refusal.
@@ -210,10 +311,10 @@ export function tooltipModel(key, values = {}) {
210
311
  * caller instead -- and if nothing survives, the caller renders no tooltip, which is the correct
211
312
  * outcome and not a crash.
212
313
  */
213
- export function tooltipModels(keys, values = {}) {
314
+ export function tooltipModels(keys, values = {}, consumer) {
214
315
  return keys
215
- .filter((k) => isNativeKey(k) ? Boolean(NATIVE_SIGNATURES[k]) : Boolean(tryGetEntrypoint(k)))
216
- .map((k) => tooltipModel(k, values));
316
+ .filter((k) => chainOf(k) !== undefined)
317
+ .map((k) => tooltipModel(k, values, consumer));
217
318
  }
218
319
  /**
219
320
  * The type row -- rule 3.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouronet/talos-registry",
3
- "version": "1.4.0",
3
+ "version": "2.1.0",
4
4
  "description": "The Ouronet callable surface, generated from the deployed contracts. Supply values; never type a function name.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",