@ouronet/ouronet-core 4.3.7 → 4.5.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 CHANGED
@@ -4,6 +4,68 @@ All notable changes to `@ouronet/ouronet-core`.
4
4
 
5
5
  This package is the historical continuation of `@ouronet/ouronet-core` v0.x–v3.3.8. v4.0.0 split it into a two-package npm workspace under `StoaChain/stoa-js` — chain-generic infrastructure moved out into [`@stoachain/stoa-core`](https://www.npmjs.com/package/@stoachain/stoa-core), this package retained the Ouronet-specific business logic. The `4.0.0` heading below is the first release after the split.
6
6
 
7
+ ## 4.5.0 — 2026-09-07
8
+
9
+ **MINOR — cross-chain gas payer switched to `stoa-xchain-gas`; live Yin-Engine gas pricing.**
10
+
11
+ ### What changed
12
+
13
+ `crossChainFunctions.ts`'s non-chain-0 gas payer moves off the legacy `kadena-xchain-gas`
14
+ account (a static `gasPrice <= 0.00000001` / `gasLimit <= 850` guard) onto StoaChain's own
15
+ `stoa-xchain-gas` account. Its guard (`pact/genesis/stoa/stoa-genesis-5.pact`) requires
16
+ `gasPrice <= UC_MinimumGasPriceANU()` — a **live floor**, not a fixed ceiling — so a hardcoded
17
+ price would eventually either be rejected by this guard's ceiling (once the floor rises above
18
+ whatever was hardcoded, that part is safe) or, more importantly, fall **below** the chain-wide
19
+ minimum price the Yin Engine enforces post-fork.
20
+
21
+ All four `gasPrice` call sites in this file now derive `{ creationTime, gasPrice }` from a
22
+ single local Yin-Engine helper instead of a hardcoded number or the static `GAS_PRICE_MIN_ANU`:
23
+
24
+ - `buildCrossChainTransfer` (legacy single-step `coin.transfer-crosschain`, self-paid gas)
25
+ - `buildCTransferAcross` — chain-0 branch (`DALOS.GAS_PAYER`, previously on the static
26
+ `anuToStoa(GAS_PRICE_MIN_ANU)`)
27
+ - `buildCTransferAcross` — non-chain-0 branch (now `stoa-xchain-gas`, was `kadena-xchain-gas`)
28
+ - `buildContinuationTransaction` — default `gasPayerAccount` (now `stoa-xchain-gas`)
29
+
30
+ The Yin-Engine helper is a **temporary local duplicate** of OuronetUI's `src/lib/gas-price.ts`
31
+ (same constants, same formula — genesis 10,000 ANU, +1 ANU/3h, capped at 400,000 ANU). It
32
+ belongs in `@stoachain/stoa-core/gas` once that package ships the same formula; that package
33
+ currently still only exports the static `GAS_PRICE_MIN_ANU = 10_000`. Consolidate then.
34
+
35
+ ### Verification
36
+
37
+ Confirmed against a live StoaChain node (not just simulated): both the initial
38
+ `C_TransferAcross` (chain-0, `DALOS.GAS_PAYER`) and the `stoa-xchain-gas`-paid continuation
39
+ landed successfully at **11,565 ANU/gas**, the live floor at time of testing.
40
+
41
+ Non-breaking. No exported function signatures changed — only the default `gasPayerAccount`
42
+ value and the internal price derivation. **787 specs pass.**
43
+
44
+ ## 4.4.0 — 2026-07-22
45
+
46
+ **MINOR — the codex writer is held at `"1.2"` while the reader keeps accepting `"1.2"` and `"1.3"`.**
47
+
48
+ ### Read this before upgrading from 4.3.6 or 4.3.7
49
+
50
+ Those two releases were a mistake, and this one corrects it. They were built from the source repo's `main`, which carried codex-**1.3** writer work that had never been released, and they were published under version numbers the old `@stoachain/ouronet-core` had already used. So `@stoachain/ouronet-core@4.3.6` and `@ouronet/ouronet-core@4.3.6` are the same number with **different behaviour**: the first writes codex `"1.2"`, the second writes `"1.3"`.
51
+
52
+ That matters because the format bump is governed by reader-before-writer discipline — the reader is widened everywhere first, and only then does the writer advance. Shipping a 1.3 writer skipped that step: an app on 4.3.6/4.3.7 produces backups that any app still on a 1.2-only reader cannot open, including `@ancientpantheon/codex`, which deliberately gates on 1.2 until its D-phase is ready.
53
+
54
+ ### What 4.4.0 does
55
+
56
+ - The **reader still accepts both** `"1.2"` and `"1.3"` — no narrowing, ever. Any 1.3 file already written by 4.3.6/4.3.7 remains readable.
57
+ - The **writer stamps `"1.2"`** again, so nothing new is produced that the rest of the ecosystem cannot read.
58
+ - Nothing is lost by this. ouronet's `PlaintextCodex` has no foreign-key source field, so the writer never emitted the 1.3-only `foreignKeys` block — its 1.2 and 1.3 output differed only in the version string.
59
+ - It is a MINOR, not a patch, because the reader's widened acceptance is genuinely new surface relative to the last honest release.
60
+
61
+ `@ouronet/ouronet-core@4.3.6` and `@4.3.7` are deprecated on npm and point here.
62
+
63
+ ### Advancing the writer later
64
+
65
+ Confirm every consumer resolves a 1.3-capable reader (4.3.6+ under this scope) and that `@ancientpantheon/codex` no longer rejects 1.3, then flip the single `version` literal in `src/codex/codec.ts` and ship it as another MINOR. `tests/v4-3-x-codec-1-3-writer.test.ts` documents that procedure and holds the invariant either way — its round-trip cases are version-agnostic on purpose.
66
+
67
+ **787 specs pass.**
68
+
7
69
  ## 4.3.7 — 2026-07-22
8
70
 
9
71
  **PATCH — chain-peer realignment, no code change.**
package/README.md CHANGED
@@ -5,17 +5,28 @@ Ouronet protocol business logic on top of [`@stoachain/stoa-core`](https://www.n
5
5
  Chain-generic infrastructure (signing, wallet, crypto, network failover, gas, guard, errors, observability, dalos, reads, pact-format helpers) lives in the sibling [`@stoachain/stoa-core`](https://www.npmjs.com/package/@stoachain/stoa-core) package — install both, or just this one if you only need the Ouronet-specific surface.
6
6
 
7
7
  > **Renamed.** This package was published as `@stoachain/ouronet-core` through 4.3.5. The
8
- > code, version line, and release history continue unbroken under the `@ouronet` scope —
8
+ > version line and release history continue under the `@ouronet` scope —
9
9
  > the published name now matches the organisation that owns it. The old name is deprecated
10
- > on npm and points here. Migration is a rename and nothing else:
10
+ > on npm and points here. At the import level, migration is a rename:
11
11
  >
12
12
  > ```diff
13
13
  > - import { serializeCodex } from "@stoachain/ouronet-core/codex";
14
14
  > + import { serializeCodex } from "@ouronet/ouronet-core/codex";
15
15
  > ```
16
+ >
17
+ > ⚠️ **But 4.3.6/4.3.7 here are NOT byte-identical to `@stoachain/ouronet-core@4.3.6`.**
18
+ > This package was built from the source repo's `main`, which carried codex-**1.3**
19
+ > work that had never been released. The old 4.3.6 writes codex `"1.2"`; these
20
+ > versions write `"1.3"`. If you consume the codex writer, that is a behaviour
21
+ > change, not a rename — verify against your reader before upgrading. Resolving
22
+ > this mislabelling is tracked as an open issue.
16
23
 
17
24
  ## Status
18
25
 
26
+ **`4.5.0` on public npmjs** — **MINOR (cross-chain gas payer switched to `stoa-xchain-gas`; live Yin-Engine gas pricing).** Released 2026-09-07. `crossChainFunctions.ts`'s non-chain-0 gas payer moves off the legacy `kadena-xchain-gas` account (a static `gasPrice <= 0.00000001` / `gasLimit <= 850` guard) onto StoaChain's own `stoa-xchain-gas` account, whose guard (`pact/genesis/stoa/stoa-genesis-5.pact`) requires `gasPrice <= UC_MinimumGasPriceANU()` — a *live floor*, not a fixed ceiling. All four `gasPrice` call sites in this file (the legacy `buildCrossChainTransfer`, both `buildCTransferAcross` branches — including the chain-0 `DALOS.GAS_PAYER` path, previously on the static `GAS_PRICE_MIN_ANU` — and `buildContinuationTransaction`'s default payer) now derive `{ creationTime, gasPrice }` from a local Yin-Engine helper instead of a hardcoded price. That helper is a temporary duplicate of OuronetUI's `src/lib/gas-price.ts` — it belongs in `@stoachain/stoa-core/gas` once that package ships the same formula (it currently still exports only the static `GAS_PRICE_MIN_ANU = 10_000`); consolidate then. Verified against a live StoaChain node: both the initial `C_TransferAcross` (chain-0, `DALOS.GAS_PAYER`) and the `stoa-xchain-gas`-paid continuation landed successfully at 11,565 ANU/gas. Non-breaking — no exported signatures changed, only the default `gasPayerAccount` value and the internal price derivation. **787 specs pass.**
27
+
28
+ **`4.4.0` on public npmjs** — **MINOR (codec writer held at 1.2; reader still accepts 1.2 and 1.3).** Released 2026-07-22. Corrects 4.3.6/4.3.7, which shipped an unreleased 1.3 *writer* and so produced backups that apps on a 1.2-only reader could not open. The reader is unchanged and still accepts both, so any 1.3 file already written stays readable; the writer stamps 1.2 again. Nothing is lost — this writer never emitted the 1.3-only `foreignKeys` block. Those two versions are deprecated and point here. **787 specs pass.**
29
+
19
30
  **`4.3.7` on public npmjs** — **PATCH (chain-peer realignment with `@stoachain/stoa-core@4.3.7` + `@stoachain/kadena-stoic-legacy@4.3.7`).** Released 2026-07-22. NO code changes in this package. The exact chain peers move 4.3.6 → 4.3.7 so consumers resolving the current chain layer get a satisfiable tree; stoa-js 4.3.7 repointed DALOS onto `@ouronet/dalos-crypto@4.0.4`. **785 specs pass.**
20
31
 
21
32
  **`4.3.6` on public npmjs** — **PATCH (atomic-triplet alignment with `@stoachain/stoa-core@4.3.6` + `@stoachain/kadena-stoic-legacy@4.3.6`).** Released 2026-06-11. NO code changes in this package; version bumped solely to maintain the atomic-triplet invariant. The v4.3.6 change (a `@stoachain/stoa-core` auto-gas-limit floor fix in `calculateAutoGasLimit`) lives entirely in `@stoachain/stoa-core`. Functionally identical to `4.3.5`. **829 specs pass.**
@@ -36,6 +47,10 @@ Chain-generic infrastructure (signing, wallet, crypto, network failover, gas, gu
36
47
 
37
48
  **`4.2.0` on public npmjs** — **MINOR (atomic with `@stoachain/stoa-core@4.2.0` + `@stoachain/kadena-stoic-legacy@4.2.0`).** Released 2026-05-09. Closes 6 audit findings: **F-ARCH-001** (Phase 1 dex god-file split — ~10 entity-oriented files + thin re-export shim), **F-ARCH-002** (Phase 2 ouro god-file split + chain/UI surgical separation — ~11 entity-oriented files + thin re-export shim), **F-ARCH-003** (Phase 3 parameterized liquidity executor — 5 thin wrappers + 1 internal `executeLiquidityOp`, LOC reduction ~600 → ~200), **F-API-002** (Phase 4 — 12 functions honor declared `Promise<T | null>` contract, return null on RPC failure instead of rethrowing), **F-API-018** (Phase 5 readonly sweep — ~50 public-type fields in `codex/types.ts` and all `*Params` interfaces), **F-TEST-006** (Phase 7 — +127 specs across 6 modules: `infoOneFunctions`, `coilFunctions`, `kpayFunctions`, `pensionFunctions`, `activateFunctions`, `guardFunctions`). NEW deliverable: [`INTEGRATION-GUIDE.md`](https://github.com/StoaChain/stoa-js/blob/main/INTEGRATION-GUIDE.md) at repo root — comprehensive cold-start consumer onboarding doc, 13 sections, full v4.0 → v4.1 → v4.2 architectural arc. 8 new `v4-2-0-*.test.ts` regression-lock files. Test count: **710 specs pass** (was ~330 in v4.1.1). Cross-reference: see [`MIGRATION-v4.2.md`](https://github.com/StoaChain/stoa-js/blob/main/MIGRATION-v4.2.md) at the monorepo root.
38
49
 
50
+ **v4.5.0** — cross-chain gas payer switched to `stoa-xchain-gas` with live Yin-Engine pricing (replaces the legacy `kadena-xchain-gas` static ceiling). Released 2026-09-07. All four `gasPrice` sites in `crossChainFunctions.ts` now derive price from the current Yin floor instead of a hardcoded value; verified landing on a live StoaChain node at 11,565 ANU/gas. No signature changes. **787 specs pass.**
51
+
52
+ **v4.4.0** — codec writer held at `"1.2"`; reader still accepts `"1.2"` and `"1.3"`. Released 2026-07-22. Corrects the 4.3.6/4.3.7 mispublish, which shipped an unreleased 1.3 writer under version numbers the old `@stoachain` package had already used. **787 specs pass.**
53
+
39
54
  **v4.3.7** — chain-peer realignment bump (exact peers on `@stoachain/stoa-core@4.3.7` + `@stoachain/kadena-stoic-legacy@4.3.7`). Released 2026-07-22. NO code changes. Required because this package pins its chain peers exactly and stoa-js now releases independently. **785 specs pass.**
40
55
 
41
56
  **v4.3.6** — atomic-triplet alignment bump (atomic with `@stoachain/stoa-core@4.3.6` + `@stoachain/kadena-stoic-legacy@4.3.6`). Released 2026-06-11. NO code changes; the v4.3.6 change (a stoa-core auto-gas-limit floor fix in `calculateAutoGasLimit`) lives in `@stoachain/stoa-core`. Functionally identical to v4.3.5. **829 specs pass.**
@@ -4,20 +4,33 @@
4
4
  * the same backup JSON (HUB's codex-import flow, CLI recovery tools,
5
5
  * etc.).
6
6
  *
7
- * The written format is now `CodexExportV1_3` the `"version": "1.3"`
8
- * string. This was an INTENTIONAL 1.2→1.3 bump made under strict
9
- * reader-before-writer discipline: `deserializeCodex` was widened to
10
- * accept BOTH "1.2" and "1.3" (and to allow-list the optional
11
- * `foreignKeys` block) BEFORE this writer began stamping "1.3". That
12
- * ordering is what makes the bump safe — every previously downloaded
13
- * `OuronetCodex_*.json` (still "1.2") keeps importing, and every new
14
- * export ("1.3") deserializes through the same reader.
7
+ * `deserializeCodex` accepts BOTH "1.2" and "1.3" (and allow-lists the
8
+ * optional `foreignKeys` block). The writer currently stamps **"1.2"**.
15
9
  *
16
- * Do NOT revert the writer to "1.2" in isolation, and do NOT narrow the
17
- * reader back to "1.2"-only: emitting a version the reader rejects (or
18
- * rejecting the version the writer emits) is a funds-loss inversion a
19
- * user's own fresh backup would fail to restore. Any future format change
20
- * must keep the reader ahead of the writer.
10
+ * THE INVARIANT: the reader must stay ahead of the writer. Emitting a
11
+ * version the reader rejects or rejecting the version the writer emits
12
+ * is a funds-loss inversion, because a user's own fresh backup would fail
13
+ * to restore. The reader is therefore always widened first, in a release
14
+ * that ships everywhere, and only then does the writer advance.
15
+ *
16
+ * Why the writer sits at "1.2" while the reader already understands "1.3":
17
+ * that is the mid-point of the migration, and it is deliberate. The reader
18
+ * half shipped; the writer half has not, because the ecosystem is not yet
19
+ * uniformly on a 1.3-capable reader — `@ancientpantheon/codex` still gates
20
+ * on 1.2. A writer that ran ahead would produce backups that the user's own
21
+ * other apps could not open. That is the exact inversion above, one step
22
+ * removed.
23
+ *
24
+ * This writer's 1.3 output was a BARE envelope — ouronet's `PlaintextCodex`
25
+ * has no foreign-key source field, so no `foreignKeys` block was ever
26
+ * emitted. The only difference between its 1.2 and 1.3 output is the
27
+ * version string itself, which is why holding at "1.2" costs no capability.
28
+ *
29
+ * TO ADVANCE THE WRITER TO "1.3": confirm every consumer resolves a core
30
+ * whose reader accepts 1.3 (that is 4.3.6+ under the @ouronet scope), and
31
+ * that `@ancientpantheon/codex` no longer rejects 1.3, then flip the
32
+ * `version` literal below and ship it as a MINOR. Do not narrow the reader
33
+ * at any point.
21
34
  *
22
35
  * All pure. No password handling in here — the BYTES INSIDE the JSON are
23
36
  * already encrypted at the codex-entry level (each wallet's `secret` field
@@ -27,19 +40,20 @@
27
40
  import type { CodexExportV1_2, CodexExportV1_3, PlaintextCodex } from "./types.js";
28
41
  /**
29
42
  * Build a codex-export payload from a PlaintextCodex. Stamps the current
30
- * `"1.3"` envelope version and `exportedAt` with the current ISO time.
43
+ * `"1.2"` envelope version and `exportedAt` with the current ISO time.
31
44
  * Returns the object — the caller stringifies it (so callers in a
32
45
  * memory-constrained environment can stream it out instead of holding the
33
46
  * whole string in RAM).
34
47
  *
35
- * The return type is the `CodexExportV1_2 | CodexExportV1_3` union so
36
- * consumers written against the historical 1.2 shape still type-check
37
- * against the widened output; the runtime value is always a 1.3 envelope.
48
+ * The return type is the `CodexExportV1_2 | CodexExportV1_3` union so this
49
+ * signature does not have to change when the writer advances to 1.3; the
50
+ * runtime value today is a 1.2 envelope. See the file header for why the
51
+ * writer trails the reader and what has to be true before it advances.
38
52
  *
39
- * The optional `foreignKeys` block is EMITTED only when the source codex
40
- * carries foreign keys. ouronet's `PlaintextCodex` has no foreign-key
41
- * source field today, so the practical output is a bare 1.3 envelope with
42
- * `foreignKeys` omitted — no mandatory empty block.
53
+ * The optional `foreignKeys` block belongs to the 1.3 shape and is never
54
+ * emitted here ouronet's `PlaintextCodex` has no foreign-key source
55
+ * field so the output carries no 1.3-only data and nothing is lost by
56
+ * stamping 1.2.
43
57
  *
44
58
  * Fields left out intentionally: `pureKeypairs`, `schemaVersion`,
45
59
  * `lastUpdatedAt`, `lastUpdatedDevice` — see CodexExportV1_3 JSDoc for
@@ -47,7 +61,7 @@ import type { CodexExportV1_2, CodexExportV1_3, PlaintextCodex } from "./types.j
47
61
  */
48
62
  export declare function buildCodexExport<KS, OA, PK, AB, UI>(codex: PlaintextCodex<KS, OA, PK, AB, UI>): CodexExportV1_2<KS, OA, AB, UI> | CodexExportV1_3<KS, OA, AB, UI>;
49
63
  /**
50
- * Stringify a PlaintextCodex into the `"1.3"` backup JSON format, the
64
+ * Stringify a PlaintextCodex into the `"1.2"` backup JSON format, the
51
65
  * exact output of OuronetUI's LocalStorageCodexAdapter.downloadAsJson.
52
66
  * Pretty-prints with 2-space indent because the file lands on disk and
53
67
  * a human occasionally opens it to sanity-check account addresses.
@@ -62,7 +76,7 @@ export declare function serializeCodex<KS, OA, PK, AB, UI>(codex: PlaintextCodex
62
76
  * Throws on:
63
77
  * - invalid JSON
64
78
  * - missing `"version"` field (malformed)
65
- * - version mismatch (not `"1.2"`)
79
+ * - version mismatch (neither `"1.2"` nor `"1.3"`)
66
80
  * - shape mismatch (kadenaWallets, ouronetWallets, addressBook not arrays; uiSettings not an object)
67
81
  *
68
82
  * The version check exists to fail-fast rather than silently mis-decoding
@@ -4,20 +4,33 @@
4
4
  * the same backup JSON (HUB's codex-import flow, CLI recovery tools,
5
5
  * etc.).
6
6
  *
7
- * The written format is now `CodexExportV1_3` the `"version": "1.3"`
8
- * string. This was an INTENTIONAL 1.2→1.3 bump made under strict
9
- * reader-before-writer discipline: `deserializeCodex` was widened to
10
- * accept BOTH "1.2" and "1.3" (and to allow-list the optional
11
- * `foreignKeys` block) BEFORE this writer began stamping "1.3". That
12
- * ordering is what makes the bump safe — every previously downloaded
13
- * `OuronetCodex_*.json` (still "1.2") keeps importing, and every new
14
- * export ("1.3") deserializes through the same reader.
7
+ * `deserializeCodex` accepts BOTH "1.2" and "1.3" (and allow-lists the
8
+ * optional `foreignKeys` block). The writer currently stamps **"1.2"**.
15
9
  *
16
- * Do NOT revert the writer to "1.2" in isolation, and do NOT narrow the
17
- * reader back to "1.2"-only: emitting a version the reader rejects (or
18
- * rejecting the version the writer emits) is a funds-loss inversion a
19
- * user's own fresh backup would fail to restore. Any future format change
20
- * must keep the reader ahead of the writer.
10
+ * THE INVARIANT: the reader must stay ahead of the writer. Emitting a
11
+ * version the reader rejects or rejecting the version the writer emits
12
+ * is a funds-loss inversion, because a user's own fresh backup would fail
13
+ * to restore. The reader is therefore always widened first, in a release
14
+ * that ships everywhere, and only then does the writer advance.
15
+ *
16
+ * Why the writer sits at "1.2" while the reader already understands "1.3":
17
+ * that is the mid-point of the migration, and it is deliberate. The reader
18
+ * half shipped; the writer half has not, because the ecosystem is not yet
19
+ * uniformly on a 1.3-capable reader — `@ancientpantheon/codex` still gates
20
+ * on 1.2. A writer that ran ahead would produce backups that the user's own
21
+ * other apps could not open. That is the exact inversion above, one step
22
+ * removed.
23
+ *
24
+ * This writer's 1.3 output was a BARE envelope — ouronet's `PlaintextCodex`
25
+ * has no foreign-key source field, so no `foreignKeys` block was ever
26
+ * emitted. The only difference between its 1.2 and 1.3 output is the
27
+ * version string itself, which is why holding at "1.2" costs no capability.
28
+ *
29
+ * TO ADVANCE THE WRITER TO "1.3": confirm every consumer resolves a core
30
+ * whose reader accepts 1.3 (that is 4.3.6+ under the @ouronet scope), and
31
+ * that `@ancientpantheon/codex` no longer rejects 1.3, then flip the
32
+ * `version` literal below and ship it as a MINOR. Do not narrow the reader
33
+ * at any point.
21
34
  *
22
35
  * All pure. No password handling in here — the BYTES INSIDE the JSON are
23
36
  * already encrypted at the codex-entry level (each wallet's `secret` field
@@ -27,19 +40,20 @@
27
40
  import { CodexUnknownFieldError } from "./errors.js";
28
41
  /**
29
42
  * Build a codex-export payload from a PlaintextCodex. Stamps the current
30
- * `"1.3"` envelope version and `exportedAt` with the current ISO time.
43
+ * `"1.2"` envelope version and `exportedAt` with the current ISO time.
31
44
  * Returns the object — the caller stringifies it (so callers in a
32
45
  * memory-constrained environment can stream it out instead of holding the
33
46
  * whole string in RAM).
34
47
  *
35
- * The return type is the `CodexExportV1_2 | CodexExportV1_3` union so
36
- * consumers written against the historical 1.2 shape still type-check
37
- * against the widened output; the runtime value is always a 1.3 envelope.
48
+ * The return type is the `CodexExportV1_2 | CodexExportV1_3` union so this
49
+ * signature does not have to change when the writer advances to 1.3; the
50
+ * runtime value today is a 1.2 envelope. See the file header for why the
51
+ * writer trails the reader and what has to be true before it advances.
38
52
  *
39
- * The optional `foreignKeys` block is EMITTED only when the source codex
40
- * carries foreign keys. ouronet's `PlaintextCodex` has no foreign-key
41
- * source field today, so the practical output is a bare 1.3 envelope with
42
- * `foreignKeys` omitted — no mandatory empty block.
53
+ * The optional `foreignKeys` block belongs to the 1.3 shape and is never
54
+ * emitted here ouronet's `PlaintextCodex` has no foreign-key source
55
+ * field so the output carries no 1.3-only data and nothing is lost by
56
+ * stamping 1.2.
43
57
  *
44
58
  * Fields left out intentionally: `pureKeypairs`, `schemaVersion`,
45
59
  * `lastUpdatedAt`, `lastUpdatedDevice` — see CodexExportV1_3 JSDoc for
@@ -47,7 +61,7 @@ import { CodexUnknownFieldError } from "./errors.js";
47
61
  */
48
62
  export function buildCodexExport(codex) {
49
63
  return {
50
- version: "1.3",
64
+ version: "1.2",
51
65
  exportedAt: new Date().toISOString(),
52
66
  kadenaWallets: codex.kadenaWallets,
53
67
  ouronetWallets: codex.ouronetWallets,
@@ -56,7 +70,7 @@ export function buildCodexExport(codex) {
56
70
  };
57
71
  }
58
72
  /**
59
- * Stringify a PlaintextCodex into the `"1.3"` backup JSON format, the
73
+ * Stringify a PlaintextCodex into the `"1.2"` backup JSON format, the
60
74
  * exact output of OuronetUI's LocalStorageCodexAdapter.downloadAsJson.
61
75
  * Pretty-prints with 2-space indent because the file lands on disk and
62
76
  * a human occasionally opens it to sanity-check account addresses.
@@ -73,7 +87,7 @@ export function serializeCodex(codex) {
73
87
  * Throws on:
74
88
  * - invalid JSON
75
89
  * - missing `"version"` field (malformed)
76
- * - version mismatch (not `"1.2"`)
90
+ * - version mismatch (neither `"1.2"` nor `"1.3"`)
77
91
  * - shape mismatch (kadenaWallets, ouronetWallets, addressBook not arrays; uiSettings not an object)
78
92
  *
79
93
  * The version check exists to fail-fast rather than silently mis-decoding
@@ -53,7 +53,7 @@ export interface CTransferAcrossParams {
53
53
  * Build a coin.C_TransferAcross transaction (step 0 of cross-chain transfer).
54
54
  *
55
55
  * Chain 0: Gas paid by Ouronet Gas Station (DALOS.GAS_PAYER).
56
- * Other chains: Gas paid by kadena-xchain-gas (unsigned).
56
+ * Other chains: Gas paid by stoa-xchain-gas (unsigned).
57
57
  */
58
58
  export declare function buildCTransferAcross(params: CTransferAcrossParams): any;
59
59
  /**
@@ -1,9 +1,49 @@
1
1
  import { Pact } from "@stoachain/kadena-stoic-legacy/client";
2
2
  import { getFailoverClient, withFailover } from "@stoachain/stoa-core/network";
3
3
  import { KADENA_NETWORK, getPactUrl, STOA_AUTONOMIC_OURONETGASSTATION, KADENA_NAMESPACE } from "../constants/index.js";
4
- import { GAS_PRICE_MIN_ANU, anuToStoa } from "@stoachain/stoa-core/gas";
5
4
  import { pactRead } from "@stoachain/stoa-core/reads";
6
5
  import { safeCreationTime, formatDecimalForPact } from "@stoachain/stoa-core/pact";
6
+ /**
7
+ * Yin Engine — StoaChain's time-varying minimum gas price.
8
+ *
9
+ * TEMPORARY LOCAL COPY. This mirrors `OuronetUI/src/lib/gas-price.ts` exactly
10
+ * (same constants, same formula) because `@stoachain/stoa-core/gas` has not
11
+ * yet been upgraded past the static `GAS_PRICE_MIN_ANU = 10_000`. It belongs
12
+ * there long-term (chain-generic, not Ouronet-specific) — consolidate this
13
+ * away once stoa-core ships it.
14
+ *
15
+ * The floor starts at 10,000 ANU at genesis and rises by 1 ANU every 3 hours,
16
+ * capped at 400,000 ANU. Source of truth: `UC_MinimumGasPriceANU` in
17
+ * `pact/stoa-coin/new-coin.pact` (evaluated against `(at "block-time"
18
+ * (chain-data))` on-chain — creationTime is a close client-side proxy for it).
19
+ *
20
+ * `stoa-xchain-gas`'s own guard (`pact/genesis/stoa/stoa-genesis-5.pact`)
21
+ * requires `gasPrice <= UC_MinimumGasPriceANU()` (an upper bound, not a
22
+ * floor) — combined with the chain-wide "must be >= the floor" rule, the
23
+ * only value that satisfies both is the floor itself. Never overpay this
24
+ * account; it pays minimum-priced redemptions only.
25
+ */
26
+ const YIN_GENESIS_TIME_S = 1771869600; // 2026-02-23T18:00:00Z — tick 0
27
+ const YIN_GENESIS_MIN_GAS_ANU = 10000;
28
+ const YIN_MAX_GAS_ANU = 400000;
29
+ const YIN_GAS_PRICE_INTERVAL_S = 10800; // 3 h per tick
30
+ function minGasPriceAnu(creationTimeS) {
31
+ const elapsed = creationTimeS - YIN_GENESIS_TIME_S;
32
+ const ticks = elapsed <= 0 ? 0 : Math.floor(elapsed / YIN_GAS_PRICE_INTERVAL_S);
33
+ return Math.min(YIN_GENESIS_MIN_GAS_ANU + ticks, YIN_MAX_GAS_ANU);
34
+ }
35
+ /** Built by string padding, never float division — avoids exponential notation / precision loss. */
36
+ function anuToStoaNumber(anu) {
37
+ return Number("0." + String(anu).padStart(12, "0"));
38
+ }
39
+ /**
40
+ * One clock read: the `creationTime` to stamp and the `gasPrice` derived
41
+ * FROM THAT EXACT VALUE, so the two can never disagree.
42
+ */
43
+ function stoaGasMeta() {
44
+ const creationTime = safeCreationTime();
45
+ return { creationTime, gasPrice: anuToStoaNumber(minGasPriceAnu(creationTime)) };
46
+ }
7
47
  /**
8
48
  * Get KDA balance for an account on a specific chain.
9
49
  * exists=false means the account row does not exist on that chain (not just zero balance).
@@ -72,20 +112,17 @@ export function buildCrossChainTransfer(params) {
72
112
  .setMeta({
73
113
  chainId: sourceChain,
74
114
  senderAccount: sender,
75
- // v3.2.3: added `creationTime: safeCreationTime()` to close audit
76
- // finding F-BUG-002. The v2.3.0 audit consolidated `safeCreationTime()`
77
- // (returns `Date.now()/1000 - 30`) precisely so client-side clock drift
78
- // doesn't trigger chain-side "creation time too far in the future"
79
- // rejections; without this field, `@kadena/client` falls back to the
80
- // raw current time and any consumer with a clock slightly ahead of
81
- // node time gets sporadic submit failures. This was the lone builder
82
- // in interactions/* that omitted the helper after the v2.3.0 sweep —
83
- // every other setMeta block (including `buildCTransferAcross` two
84
- // functions below) already includes it.
85
- creationTime: safeCreationTime(),
115
+ // v3.2.3: `creationTime` closes audit finding F-BUG-002 — without it
116
+ // `@kadena/client` falls back to raw current time and a client clock
117
+ // slightly ahead of node time gets sporadic submit failures.
118
+ // `stoaGasMeta()` supplies both `creationTime` (via `safeCreationTime()`)
119
+ // and the `gasPrice` derived from that exact value, per the Yin Engine
120
+ // (see the module-level comment above) the chain-wide minimum price
121
+ // rises over time, so a hardcoded `gasPrice` would eventually be
122
+ // rejected as underpriced.
86
123
  gasLimit: 2500,
87
- gasPrice: 0.00000001,
88
124
  ttl: 28800,
125
+ ...stoaGasMeta(),
89
126
  })
90
127
  .setNetworkId(KADENA_NETWORK)
91
128
  .createTransaction();
@@ -95,7 +132,7 @@ export function buildCrossChainTransfer(params) {
95
132
  * Build a coin.C_TransferAcross transaction (step 0 of cross-chain transfer).
96
133
  *
97
134
  * Chain 0: Gas paid by Ouronet Gas Station (DALOS.GAS_PAYER).
98
- * Other chains: Gas paid by kadena-xchain-gas (unsigned).
135
+ * Other chains: Gas paid by stoa-xchain-gas (unsigned).
99
136
  */
100
137
  export function buildCTransferAcross(params) {
101
138
  const { sender, receiver, receiverGuard, amount, sourceChain, targetChain, senderPublicKey, gasStationPublicKey, } = params;
@@ -118,18 +155,19 @@ export function buildCTransferAcross(params) {
118
155
  .setMeta({
119
156
  chainId: "0",
120
157
  senderAccount: STOA_AUTONOMIC_OURONETGASSTATION,
121
- creationTime: safeCreationTime(),
122
158
  gasLimit: 2500,
123
- gasPrice: anuToStoa(GAS_PRICE_MIN_ANU),
124
159
  ttl: 28800,
160
+ ...stoaGasMeta(),
125
161
  })
126
162
  .setNetworkId(KADENA_NETWORK)
127
163
  .createTransaction();
128
164
  }
129
165
  else {
130
- // Non-chain-0: kadena-xchain-gas pays gas.
166
+ // Non-chain-0: stoa-xchain-gas pays gas (StoaChain's own xchain gas
167
+ // station — replaces the legacy `kadena-xchain-gas` account).
131
168
  // Its guard uses coin.gas-only (no signature needed) BUT requires:
132
- // - gasPrice <= 0.00000001
169
+ // - gasPrice <= UC_MinimumGasPriceANU() (the live Yin floor — an upper
170
+ // bound, not a static ceiling; see the module-level comment above)
133
171
  // - gasLimit <= 850
134
172
  // No signer needed for gas — only sender signs TRANSFER_XCHAIN cap.
135
173
  return Pact.builder
@@ -140,10 +178,10 @@ export function buildCTransferAcross(params) {
140
178
  ])
141
179
  .setMeta({
142
180
  chainId: sourceChain,
143
- senderAccount: "kadena-xchain-gas",
144
- gasLimit: 850, // must be 850 for kadena-xchain-gas guard
145
- gasPrice: 0.00000001, // must be ≤ 0.00000001 for kadena-xchain-gas guard
181
+ senderAccount: "stoa-xchain-gas",
182
+ gasLimit: 850, // must be <= 850 for the stoa-xchain-gas guard
146
183
  ttl: 28800,
184
+ ...stoaGasMeta(),
147
185
  })
148
186
  .setNetworkId(KADENA_NETWORK)
149
187
  .createTransaction();
@@ -326,7 +364,7 @@ export async function pollSpvProof(requestKey, sourceChain, targetChain, maxAtte
326
364
  * Build a continuation transaction for step 2 of cross-chain transfer
327
365
  * This can be executed by anyone - no signature required from original sender
328
366
  */
329
- export function buildContinuationTransaction(pactId, proof, targetChain, gasPayerAccount = "kadena-xchain-gas") {
367
+ export function buildContinuationTransaction(pactId, proof, targetChain, gasPayerAccount = "stoa-xchain-gas") {
330
368
  const transaction = Pact.builder
331
369
  .continuation({
332
370
  pactId,
@@ -337,9 +375,9 @@ export function buildContinuationTransaction(pactId, proof, targetChain, gasPaye
337
375
  .setMeta({
338
376
  chainId: targetChain,
339
377
  senderAccount: gasPayerAccount,
340
- gasLimit: 850,
341
- gasPrice: 0.00000001,
378
+ gasLimit: 850, // must be <= 850 for the stoa-xchain-gas guard
342
379
  ttl: 28800,
380
+ ...stoaGasMeta(),
343
381
  })
344
382
  .setNetworkId(KADENA_NETWORK)
345
383
  .createTransaction();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouronet/ouronet-core",
3
- "version": "4.3.7",
3
+ "version": "4.5.0",
4
4
  "description": "Ouronet-specific business logic for the Ouronet protocol on StoaChain — interactions/* read+execute functions for the ouronet-ns Pact modules, the Codex encrypted-multi-wallet format, Ouronet protocol accounts and tokens. Depends on @stoachain/stoa-core for chain-generic infrastructure (signing, wallet, crypto, network, etc.).",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",