@ouronet/talos-registry 1.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/CHANGELOG.md +49 -0
- package/README.md +168 -0
- package/dist/build.d.ts +41 -0
- package/dist/build.js +100 -0
- package/dist/caps.d.ts +42 -0
- package/dist/caps.js +44 -0
- package/dist/data/registry.json +1 -0
- package/dist/format.d.ts +30 -0
- package/dist/format.js +113 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +15 -0
- package/dist/plan.d.ts +32 -0
- package/dist/plan.js +79 -0
- package/dist/registry.d.ts +34 -0
- package/dist/registry.js +69 -0
- package/dist/types.d.ts +163 -0
- package/dist/types.js +8 -0
- package/package.json +49 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
## 1.1.0 — 2026-09-26
|
|
2
|
+
|
|
3
|
+
**Ghost values: 40 account-shaped parameters stopped rendering as `"example"`.**
|
|
4
|
+
|
|
5
|
+
Surface `67c0979111eb156b` → `7e59e59d4c5b7257`. No contract changed — the module hashes are
|
|
6
|
+
identical, verified by `_registrylive.py` (12 modules, 0 drifted). What moved is the ghost
|
|
7
|
+
dictionary, and the surface hash moves with it because the snapshot is the whole artefact.
|
|
8
|
+
|
|
9
|
+
**Why it mattered.** A consumer previewing `ATS|C_Coil` got
|
|
10
|
+
`No value found in table … for key: example` — true, and useless. The ghost dictionary keyed
|
|
11
|
+
account values on `patron` / `executor` / `executee` / `account` / `buyer` / `receiver`, and the
|
|
12
|
+
contracts' own vocabulary is much wider: `coiler`, `curler`, `brumator`, `constricter`, `culler`,
|
|
13
|
+
`fueler`, `kickstarter`, `awaker`, `hibernator`, `wrapper`, `unwrapper`, `vester`, `redeemer`,
|
|
14
|
+
`transmuter`, `donor`, `owner-konto`, `redemption-payer` … Every one fell through to the generic
|
|
15
|
+
string ghost.
|
|
16
|
+
|
|
17
|
+
**Not guessed.** The names were matched against `_executorplan.ACCT` — the curated account-vs-
|
|
18
|
+
entity vocabulary the patron/executor canon sweep already maintains in the Pact repo, for exactly
|
|
19
|
+
this distinction. Reusing it means there is one list, not two that drift.
|
|
20
|
+
|
|
21
|
+
**227 slots are still generic, and deliberately so.** They are entity ids — `fvt-id`, `pool-id`,
|
|
22
|
+
`score-id`, `collectable-id` — where a plausible-looking fake would produce a confident number for
|
|
23
|
+
an entity nobody owns. An obviously-fake `"example"` is the better failure.
|
|
24
|
+
|
|
25
|
+
### `params` on every spec
|
|
26
|
+
|
|
27
|
+
Consumers merging real values into a preview need the **preview's** parameter names, and
|
|
28
|
+
**410 of 423 previews take a different parameter list from their entrypoint** —
|
|
29
|
+
`INFO_DPTF|Transfer` is `(patron id sender receiver transfer-amount)` against `C_Transfer`'s
|
|
30
|
+
`(patron executor executee id transfer-amount method)`. Merging on the execution signature would
|
|
31
|
+
put an account in an `id` slot on 97% of calls, silently, with a cost beside it.
|
|
32
|
+
|
|
33
|
+
# @ouronet/talos-registry
|
|
34
|
+
|
|
35
|
+
## 1.0.0
|
|
36
|
+
|
|
37
|
+
First release. 423 entrypoints, 428 previews, surface `67c0979111eb156b`, generated against
|
|
38
|
+
mainnet by `REPL/tools/_registry.py --probe`.
|
|
39
|
+
|
|
40
|
+
* `buildCall` / `buildPreviewCall` / `buildGhostCall` — arguments by NAME, rendered in the
|
|
41
|
+
contract's declared order and checked against the deployed signature.
|
|
42
|
+
* `planCall` / `explainCall` — preflight reads, capability reader, signers, transaction count
|
|
43
|
+
and the warnings that live in different keys of the record.
|
|
44
|
+
* `capabilityRecipe` / `parseCapabilities` — the four launchpad buys.
|
|
45
|
+
* `formatForType` — Pact literal rules: decimals never bare, lists space-separated, glyph
|
|
46
|
+
strings verbatim, guards as `(read-keyset "name")`.
|
|
47
|
+
* Full types for the record.
|
|
48
|
+
|
|
49
|
+
450 tests, including one per entrypoint asserting its ghost builds.
|
package/README.md
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# @ouronet/talos-registry
|
|
2
|
+
|
|
3
|
+
The Ouronet callable surface, generated from the **deployed** contracts.
|
|
4
|
+
|
|
5
|
+
A consumer supplies **values**. It never types a function name, an argument order, or an arity.
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { buildCall, planCall, explainCall } from "@ouronet/talos-registry";
|
|
9
|
+
|
|
10
|
+
buildCall("TS01-C1.DPTF|C_Transfer", {
|
|
11
|
+
patron: "Σ.…", executor: "Σ.…", executee: "Σ.…",
|
|
12
|
+
id: "OURO-8Nh-JO8JO4F5", "transfer-amount": 1, method: false,
|
|
13
|
+
});
|
|
14
|
+
// (ouronet-ns.TS01-C1.DPTF|C_Transfer "Σ.…" "Σ.…" "Σ.…" "OURO-8Nh-JO8JO4F5" 1.0 false)
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Note `1` became `1.0`. Pact's decimal lexer rejects a bare integer in a decimal slot, and that
|
|
18
|
+
is the least interesting thing this package stops you getting wrong.
|
|
19
|
+
|
|
20
|
+
**This build:** 423 entrypoints, 428 previews, surface `7e59e59d4c5b7257`, generated against mainnet.
|
|
21
|
+
Every figure in this file is asserted by `tests/readme.test.ts` against the bundled snapshot, so
|
|
22
|
+
a stale number fails the suite rather than misleading a reader.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Why it exists
|
|
27
|
+
|
|
28
|
+
Every consumer bug that led to this was a hand-written Pact template that drifted from the
|
|
29
|
+
contract, and **none of them failed loudly**:
|
|
30
|
+
|
|
31
|
+
- **A missing module member is a RESOLUTION error.** `try` cannot catch it, so consumers render
|
|
32
|
+
it as a default. Thirty-four stale names made Awake and Slumber report "no hibernated nonces"
|
|
33
|
+
against three live ones — it looked like missing data, not a broken call.
|
|
34
|
+
- **A short call is not rejected.** Pact **partially applies** and yields a closure. A launchpad
|
|
35
|
+
read passing four arguments to a five-parameter reader returned *"Evaluation did not reduce to
|
|
36
|
+
a value"*; the capabilities came back null and the purchase silently could not be funded.
|
|
37
|
+
- **An argument can move without the count changing.** `SWP|C_ChangeOwnership` kept four
|
|
38
|
+
parameters and moved the pool id to last. No arity check catches that.
|
|
39
|
+
|
|
40
|
+
`buildCall` takes arguments **by name** and renders them in the contract's declared order, so
|
|
41
|
+
both classes are impossible.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## What an entry knows
|
|
46
|
+
|
|
47
|
+
Nine things, for all 423 entrypoints:
|
|
48
|
+
|
|
49
|
+
| | |
|
|
50
|
+
|---|---|
|
|
51
|
+
| **shape** | parameters in order, with declared types, and the return type when one is declared |
|
|
52
|
+
| **preview** | the `INFO_` reader that prices the call — it lives on a *different module* |
|
|
53
|
+
| **ownership** | whose key the caller must sign for, marked `ALWAYS` or `CONDITIONAL` |
|
|
54
|
+
| **provenance** | deployed vs repo-only, module hash, and any deployed/repo divergence |
|
|
55
|
+
| **capabilities** | the reader that computes required `coin.TRANSFER` caps, and how to map its arguments |
|
|
56
|
+
| **sponsorship** | whether the gas station pays — and for a `defpact`, that it pays for step 0 only |
|
|
57
|
+
| **formula** | same field: the capability reader *is* the formula, because the amounts cannot be derived client-side |
|
|
58
|
+
| **ghost** | a worked example for every parameter, shapes read from mainnet |
|
|
59
|
+
| **execution** | how it runs — below |
|
|
60
|
+
|
|
61
|
+
### Execution modes
|
|
62
|
+
|
|
63
|
+
| mode | n | meaning |
|
|
64
|
+
|---|---|---|
|
|
65
|
+
| `direct` | 396 | the inputs suffice |
|
|
66
|
+
| `defpact` | 10 | multi-transaction by **continuation** |
|
|
67
|
+
| `indirect-parallel` | 9 | a preflight cut into slices — order-independent, fire together |
|
|
68
|
+
| `indirect-single` | 5 | one transaction, but a preflight supplies an argument |
|
|
69
|
+
| `indirect-sequential` | 3 | a preflight reports progress; each call advances a **stored cursor** |
|
|
70
|
+
|
|
71
|
+
The last two both wear the `p` suffix in Pact and **only one is parallel**. Firing a cursor
|
|
72
|
+
pager's pages concurrently races its own counter. `planCall()` says which you have.
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Three things that surprise people
|
|
77
|
+
|
|
78
|
+
**Continuations are not gas-sponsored.** Chainweb injects `exec-code` only for `exec` payloads.
|
|
79
|
+
`DALOS.GAS_PAYER` binds it *eagerly*, so on a `cont` it raises before any check runs. Step 0 is
|
|
80
|
+
sponsored; the **customer account** pays every later step. All ten `defpact` entrypoints have a
|
|
81
|
+
single-transaction twin with a byte-identical parameter list — `preferInstead` names it, and
|
|
82
|
+
`buildCall` refuses the defpact route unless you pass `{ allowUnsupported: true }`.
|
|
83
|
+
|
|
84
|
+
**An entity id is not an account.** `swpair` is a pool id; nobody holds its key. The account is
|
|
85
|
+
whatever `UR_OwnerKonto` returns for it. `ownership.requires[].via` says `"parameter"` or
|
|
86
|
+
`"reader"`, and never conflates them.
|
|
87
|
+
|
|
88
|
+
**Four names resolve to two modules.** `SWP|C_AddFrozenLiquidity` and three siblings exist on
|
|
89
|
+
both `TS01-C3` (direct) and `TS01-CP` (defpact), with identical signatures. `resolveByName()`
|
|
90
|
+
**refuses** rather than guessing — picking wrong is silent in both directions.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## API
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
// lookup
|
|
98
|
+
getEntrypoint(key) // throws, and names near matches, on a stale key
|
|
99
|
+
resolveByName(fn) // refuses an ambiguous bare name
|
|
100
|
+
entrypointKeys() / modules() / entrypointsOfModule(m)
|
|
101
|
+
registry / surfaceHash / namespace
|
|
102
|
+
|
|
103
|
+
// build — checked against the deployed signature
|
|
104
|
+
buildCall(key, argsByName, opts?)
|
|
105
|
+
buildPreviewCall(key, argsByName, opts?)
|
|
106
|
+
buildGhostCall(key) // the worked example. Illustrative, never submittable.
|
|
107
|
+
|
|
108
|
+
// plan
|
|
109
|
+
planCall(key) -> CallPlan // preflight, capabilities, signers, tx count, warnings
|
|
110
|
+
explainCall(key) -> string // the same, as text
|
|
111
|
+
|
|
112
|
+
// capabilities
|
|
113
|
+
capabilityRecipe(key) // null when GAS_PAYER alone suffices
|
|
114
|
+
parseCapabilities(strings) // <(coin.TRANSFER "a" "b" 1.0)> -> signer args
|
|
115
|
+
|
|
116
|
+
// rendering
|
|
117
|
+
formatForType(value, declaredType)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### A launchpad buy, end to end
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
const KEY = "TS02-CPAD.SPARK|C_BuySparks";
|
|
124
|
+
const recipe = capabilityRecipe(KEY)!; // DEMIPAD-SPARK.URC_Acquire
|
|
125
|
+
|
|
126
|
+
const raw = await pactRead(buildCall(recipe.reader, {
|
|
127
|
+
buyer, amount: sparks, "iz-native": true, slippage: 1.0,
|
|
128
|
+
}));
|
|
129
|
+
const caps = parseCapabilities(raw); // attach to the PAYER signer
|
|
130
|
+
|
|
131
|
+
const code = buildCall(KEY, {
|
|
132
|
+
patron, buyer, "sparks-amount": sparks, "iz-native": true, "max-cost": maxCost,
|
|
133
|
+
});
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Call the reader. **Do not recompute the amounts** — they depend on live price, the
|
|
137
|
+
native/wrapped split and a slippage pad, and a self-derived figure signs a capability the
|
|
138
|
+
contract will not match.
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## The snapshot is pinned, deliberately
|
|
143
|
+
|
|
144
|
+
The registry is **bundled at build time**, not fetched at runtime. Refreshing live would add a
|
|
145
|
+
trust surface (whatever a node returns) and a failure mode (offline means no registry) that a
|
|
146
|
+
pinned artefact does not have. `surfaceHash` identifies exactly which surface a build was
|
|
147
|
+
compiled against.
|
|
148
|
+
|
|
149
|
+
`npm run sync` copies it from the Pact repo and **refuses** a snapshot that was not generated
|
|
150
|
+
against the chain, or that carries deployed/repo divergences — a truncated or repo-only copy
|
|
151
|
+
would be worse than none, because consumers would validate against it and get confident wrong
|
|
152
|
+
answers.
|
|
153
|
+
|
|
154
|
+
Regenerate upstream with `python3 REPL/tools/_registry.py --probe`, then `npm run build` here.
|
|
155
|
+
|
|
156
|
+
## Ghost values
|
|
157
|
+
|
|
158
|
+
Every parameter has a worked example. They are keyed by **parameter name**, then by type, then
|
|
159
|
+
derived from the contract's own `defschema` — 423 entrypoints share 2,182 slots but only 276
|
|
160
|
+
distinct names, and four of those cover half of them. Authoring per entrypoint would mean
|
|
161
|
+
writing the same account into 416 `patron` slots by hand.
|
|
162
|
+
|
|
163
|
+
Every id was read from **mainnet**, so the *shape* is real: you can see that a swpair is a
|
|
164
|
+
four-segment pipe-joined string and an account is a glyph string, not a `k:` address.
|
|
165
|
+
|
|
166
|
+
**They are not submittable.** The accounts are not yours to sign for, and an amount of `1.0` is
|
|
167
|
+
a placeholder. A preflight-fed parameter is `null` with a note naming the read — the registry
|
|
168
|
+
will not invent a value it has just told you cannot be constructed.
|
package/dist/build.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export interface BuildOptions {
|
|
2
|
+
/**
|
|
3
|
+
* Build even when `execution.supported` is false. The ten `defpact` entrypoints each have a
|
|
4
|
+
* single-transaction twin with an identical parameter list, and the owner's 2026-09-15 ruling
|
|
5
|
+
* is that the defpact paths are historical. Pass this only when exercising one deliberately.
|
|
6
|
+
*/
|
|
7
|
+
allowUnsupported?: boolean;
|
|
8
|
+
/** Override the namespace. Defaults to the registry's own (`ouronet-ns`). */
|
|
9
|
+
namespace?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Build the Pact code for a call, CHECKED against the deployed signature.
|
|
13
|
+
*
|
|
14
|
+
* This is the whole point of the package. Every consumer bug this registry was built after was
|
|
15
|
+
* a hand-written template that drifted from the contract:
|
|
16
|
+
*
|
|
17
|
+
* * five builders passed three arguments to entrypoints that take four, after the
|
|
18
|
+
* patron/executor sweep added `executor` -- the UI stopped compiling and nobody noticed
|
|
19
|
+
* because the repo's typecheck ran against a config that checked zero files;
|
|
20
|
+
* * a launchpad read passed four arguments to a five-parameter reader. Pact does not raise
|
|
21
|
+
* "wrong arity" for that: it PARTIALLY APPLIES and yields a closure, so the caller saw
|
|
22
|
+
* "Evaluation did not reduce to a value" and the buy silently could not be funded.
|
|
23
|
+
*
|
|
24
|
+
* Arguments are supplied BY NAME and rendered in the contract's declared order, so an argument
|
|
25
|
+
* cannot be transposed either -- `C_ChangeOwnership` moved its pool id to last while keeping
|
|
26
|
+
* the same arity, which no count check would catch.
|
|
27
|
+
*/
|
|
28
|
+
export declare function buildCall(key: string, args: Record<string, unknown>, opts?: BuildOptions): string;
|
|
29
|
+
/**
|
|
30
|
+
* Build the cost-preview (`INFO_`) call for an entrypoint.
|
|
31
|
+
*
|
|
32
|
+
* The preview lives on a DIFFERENT module by design, and assuming otherwise is how a consumer
|
|
33
|
+
* ended up calling INFO-ZERO for previews that had moved to INFO-ONE. The pairing is recorded,
|
|
34
|
+
* not guessed.
|
|
35
|
+
*/
|
|
36
|
+
export declare function buildPreviewCall(key: string, args: Record<string, unknown>, opts?: BuildOptions): string;
|
|
37
|
+
/** Build the example call from the registry's ghost values. Illustrative, never submittable. */
|
|
38
|
+
export declare function buildGhostCall(key: string): {
|
|
39
|
+
code: string;
|
|
40
|
+
warning: string;
|
|
41
|
+
};
|
package/dist/build.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { formatForType } from "./format.js";
|
|
2
|
+
import { getEntrypoint, namespace, RegistryError, registry } from "./registry.js";
|
|
3
|
+
/**
|
|
4
|
+
* Build the Pact code for a call, CHECKED against the deployed signature.
|
|
5
|
+
*
|
|
6
|
+
* This is the whole point of the package. Every consumer bug this registry was built after was
|
|
7
|
+
* a hand-written template that drifted from the contract:
|
|
8
|
+
*
|
|
9
|
+
* * five builders passed three arguments to entrypoints that take four, after the
|
|
10
|
+
* patron/executor sweep added `executor` -- the UI stopped compiling and nobody noticed
|
|
11
|
+
* because the repo's typecheck ran against a config that checked zero files;
|
|
12
|
+
* * a launchpad read passed four arguments to a five-parameter reader. Pact does not raise
|
|
13
|
+
* "wrong arity" for that: it PARTIALLY APPLIES and yields a closure, so the caller saw
|
|
14
|
+
* "Evaluation did not reduce to a value" and the buy silently could not be funded.
|
|
15
|
+
*
|
|
16
|
+
* Arguments are supplied BY NAME and rendered in the contract's declared order, so an argument
|
|
17
|
+
* cannot be transposed either -- `C_ChangeOwnership` moved its pool id to last while keeping
|
|
18
|
+
* the same arity, which no count check would catch.
|
|
19
|
+
*/
|
|
20
|
+
export function buildCall(key, args, opts = {}) {
|
|
21
|
+
const ep = getEntrypoint(key);
|
|
22
|
+
if (ep.source === "repo-only") {
|
|
23
|
+
throw new RegistryError(`"${key}" is not callable: no live code is deployed for it. ${ep.note ?? ""}`.trim());
|
|
24
|
+
}
|
|
25
|
+
if (ep.execution.supported === false && !opts.allowUnsupported) {
|
|
26
|
+
throw new RegistryError(`"${key}" is not the supported route. ${ep.execution.supportedNote ?? ""} ` +
|
|
27
|
+
`Pass { allowUnsupported: true } to build it anyway.`);
|
|
28
|
+
}
|
|
29
|
+
const declared = ep.params.map(p => p.name);
|
|
30
|
+
const given = Object.keys(args);
|
|
31
|
+
const missing = declared.filter(n => !(n in args));
|
|
32
|
+
const extra = given.filter(n => !declared.includes(n));
|
|
33
|
+
if (missing.length || extra.length) {
|
|
34
|
+
const lines = [`Arguments do not match ${key}.`];
|
|
35
|
+
if (missing.length)
|
|
36
|
+
lines.push(` missing: ${missing.join(", ")}`);
|
|
37
|
+
if (extra.length)
|
|
38
|
+
lines.push(` unexpected: ${extra.join(", ")}`);
|
|
39
|
+
lines.push(` declared: (${ep.params.map(p => `${p.name}:${p.type}`).join(" ")})`);
|
|
40
|
+
// Say it plainly: a short call does not error on chain, it partially applies.
|
|
41
|
+
if (missing.length)
|
|
42
|
+
lines.push(` A short call is NOT rejected by Pact -- it partially applies and yields a closure, ` +
|
|
43
|
+
`so the transaction "succeeds" having done nothing.`);
|
|
44
|
+
throw new RegistryError(lines.join("\n"));
|
|
45
|
+
}
|
|
46
|
+
// A preflight-fed parameter must carry the READ's output. The registry marks these because a
|
|
47
|
+
// client cannot construct them; passing null here would build a call that cannot work.
|
|
48
|
+
for (const fed of ep.execution.fedParams ?? []) {
|
|
49
|
+
if (args[fed.param] === null || args[fed.param] === undefined) {
|
|
50
|
+
throw new RegistryError(`"${fed.param}" is the output of a preflight read and must be supplied.\n` +
|
|
51
|
+
` run: ${fed.producedBy.join(" or ")}\n` +
|
|
52
|
+
` ${ep.execution.note}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const ns = opts.namespace ?? namespace;
|
|
56
|
+
const rendered = ep.params.map(p => formatForType(args[p.name], p.type));
|
|
57
|
+
return `(${ns}.${key} ${rendered.join(" ")})`;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Build the cost-preview (`INFO_`) call for an entrypoint.
|
|
61
|
+
*
|
|
62
|
+
* The preview lives on a DIFFERENT module by design, and assuming otherwise is how a consumer
|
|
63
|
+
* ended up calling INFO-ZERO for previews that had moved to INFO-ONE. The pairing is recorded,
|
|
64
|
+
* not guessed.
|
|
65
|
+
*/
|
|
66
|
+
export function buildPreviewCall(key, args, opts = {}) {
|
|
67
|
+
const ep = getEntrypoint(key);
|
|
68
|
+
if (!ep.preview) {
|
|
69
|
+
throw new RegistryError(`"${key}" has no cost preview. ${ep.previewMissing ?? ""}`.trim());
|
|
70
|
+
}
|
|
71
|
+
const pv = registry.previews[ep.preview];
|
|
72
|
+
const declared = pv.params.map(p => p.name);
|
|
73
|
+
const missing = declared.filter(n => !(n in args));
|
|
74
|
+
if (missing.length) {
|
|
75
|
+
throw new RegistryError(`Preview ${ep.preview} is missing: ${missing.join(", ")}\n` +
|
|
76
|
+
` declared: (${pv.params.map(p => `${p.name}:${p.type}`).join(" ")})\n` +
|
|
77
|
+
` note: a preview's parameters are NOT always the entrypoint's -- it may omit a ` +
|
|
78
|
+
`ceiling the user chooses, or name the same slot differently.`);
|
|
79
|
+
}
|
|
80
|
+
const ns = opts.namespace ?? namespace;
|
|
81
|
+
const rendered = pv.params.map(p => formatForType(args[p.name], p.type));
|
|
82
|
+
return `(${ns}.${ep.preview} ${rendered.join(" ")})`;
|
|
83
|
+
}
|
|
84
|
+
/** Build the example call from the registry's ghost values. Illustrative, never submittable. */
|
|
85
|
+
export function buildGhostCall(key) {
|
|
86
|
+
const ep = getEntrypoint(key);
|
|
87
|
+
const fed = new Set((ep.execution.fedParams ?? []).map(f => f.param));
|
|
88
|
+
const args = {};
|
|
89
|
+
for (const p of ep.params) {
|
|
90
|
+
args[p.name] = fed.has(p.name)
|
|
91
|
+
? `<from ${(ep.execution.preflight ?? ["preflight"]).join(" / ")}>`
|
|
92
|
+
: ep.ghost.args[p.name];
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
code: `(${namespace}.${key} ${ep.params
|
|
96
|
+
.map(p => (fed.has(p.name) ? String(args[p.name]) : formatForType(args[p.name], p.type)))
|
|
97
|
+
.join(" ")})`,
|
|
98
|
+
warning: ep.ghost.warning,
|
|
99
|
+
};
|
|
100
|
+
}
|
package/dist/caps.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/** One capability, parsed out of what a capability reader returned. */
|
|
2
|
+
export interface ParsedCapability {
|
|
3
|
+
/** fully-qualified, e.g. `coin.TRANSFER` */
|
|
4
|
+
name: string;
|
|
5
|
+
/** positional arguments, already typed for a Kadena signer */
|
|
6
|
+
args: (string | {
|
|
7
|
+
decimal: string;
|
|
8
|
+
})[];
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Parse the strings a capability reader returns.
|
|
12
|
+
*
|
|
13
|
+
* `URC_Acquire` returns `[string]`, each wrapped in angle brackets:
|
|
14
|
+
*
|
|
15
|
+
* <(coin.TRANSFER "from" "to" 9.595000000000)>
|
|
16
|
+
*
|
|
17
|
+
* The brackets are the contract's own quoting; they are not part of the capability. Parsing
|
|
18
|
+
* this by hand is where a consumer drops a leg and signs for less than the transaction moves.
|
|
19
|
+
*/
|
|
20
|
+
export declare function parseCapability(raw: string): ParsedCapability;
|
|
21
|
+
export declare function parseCapabilities(raws: readonly string[]): ParsedCapability[];
|
|
22
|
+
/** What a caller must do to obtain this entrypoint's extra capabilities. */
|
|
23
|
+
export interface CapabilityRecipe {
|
|
24
|
+
reader: string;
|
|
25
|
+
/** reader parameter -> where its value comes from */
|
|
26
|
+
argsFrom: Record<string, string>;
|
|
27
|
+
readerParams: {
|
|
28
|
+
name: string;
|
|
29
|
+
type: string;
|
|
30
|
+
}[];
|
|
31
|
+
attachTo: string;
|
|
32
|
+
note: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The recipe for an entrypoint's required capabilities, or null when GAS_PAYER is enough.
|
|
36
|
+
*
|
|
37
|
+
* Only four entrypoints need this -- the launchpad buys -- and the reader that computes them
|
|
38
|
+
* lives on the SALE module, not on the Talos module the entrypoint belongs to. Looking in the
|
|
39
|
+
* entrypoint's own module finds nothing, which reads as "no capabilities required" rather than
|
|
40
|
+
* as a failed lookup.
|
|
41
|
+
*/
|
|
42
|
+
export declare function capabilityRecipe(key: string): CapabilityRecipe | null;
|
package/dist/caps.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { getEntrypoint, RegistryError } from "./registry.js";
|
|
2
|
+
/**
|
|
3
|
+
* Parse the strings a capability reader returns.
|
|
4
|
+
*
|
|
5
|
+
* `URC_Acquire` returns `[string]`, each wrapped in angle brackets:
|
|
6
|
+
*
|
|
7
|
+
* <(coin.TRANSFER "from" "to" 9.595000000000)>
|
|
8
|
+
*
|
|
9
|
+
* The brackets are the contract's own quoting; they are not part of the capability. Parsing
|
|
10
|
+
* this by hand is where a consumer drops a leg and signs for less than the transaction moves.
|
|
11
|
+
*/
|
|
12
|
+
export function parseCapability(raw) {
|
|
13
|
+
const cleaned = raw.replace(/^\s*<\s*/, "").replace(/\s*>\s*$/, "").trim();
|
|
14
|
+
const m = cleaned.match(/^\(([^\s()]+)\s+"([^"]*)"\s+"([^"]*)"\s+([\d.]+)\)$/);
|
|
15
|
+
if (!m) {
|
|
16
|
+
throw new RegistryError(`Unrecognised capability string: ${raw}\n` +
|
|
17
|
+
` expected: <(NAME "from" "to" amount)>`);
|
|
18
|
+
}
|
|
19
|
+
const [, name, from, to, amount] = m;
|
|
20
|
+
return { name, args: [from, to, { decimal: amount }] };
|
|
21
|
+
}
|
|
22
|
+
export function parseCapabilities(raws) {
|
|
23
|
+
return raws.map(parseCapability);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The recipe for an entrypoint's required capabilities, or null when GAS_PAYER is enough.
|
|
27
|
+
*
|
|
28
|
+
* Only four entrypoints need this -- the launchpad buys -- and the reader that computes them
|
|
29
|
+
* lives on the SALE module, not on the Talos module the entrypoint belongs to. Looking in the
|
|
30
|
+
* entrypoint's own module finds nothing, which reads as "no capabilities required" rather than
|
|
31
|
+
* as a failed lookup.
|
|
32
|
+
*/
|
|
33
|
+
export function capabilityRecipe(key) {
|
|
34
|
+
const ec = getEntrypoint(key).externalCaps;
|
|
35
|
+
if (!ec)
|
|
36
|
+
return null;
|
|
37
|
+
return {
|
|
38
|
+
reader: ec.computedBy,
|
|
39
|
+
argsFrom: ec.argsFrom,
|
|
40
|
+
readerParams: ec.readerParams,
|
|
41
|
+
attachTo: ec.attachTo,
|
|
42
|
+
note: ec.note,
|
|
43
|
+
};
|
|
44
|
+
}
|