@mysten-incubation/devstack 0.2.0 → 0.4.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.
Files changed (159) hide show
  1. package/README.md +1 -1
  2. package/dashboard-ui/assets/{grpc-a4usE3Nk.js → grpc-CpkDu4SA.js} +1 -1
  3. package/dashboard-ui/assets/{index-CRYJ4pru.js → index-jLPRmjst.js} +39 -33
  4. package/dashboard-ui/index.html +1 -1
  5. package/dist/api/inference-network.d.mts +5 -5
  6. package/dist/api/inference-network.mjs +5 -5
  7. package/dist/api/inference-network.mjs.map +1 -1
  8. package/dist/api/run-stack-internal.mjs +2 -3
  9. package/dist/api/run-stack-internal.mjs.map +1 -1
  10. package/dist/build-integrations/dapp-kit/index.d.mts +12 -0
  11. package/dist/build-integrations/dapp-kit/index.mjs +14 -1
  12. package/dist/build-integrations/dapp-kit/index.mjs.map +1 -1
  13. package/dist/build-integrations/playwright/config.d.mts +2 -1
  14. package/dist/build-integrations/playwright/config.mjs +1 -1
  15. package/dist/build-integrations/playwright/config.mjs.map +1 -1
  16. package/dist/build-integrations/playwright/errors.d.mts +1 -1
  17. package/dist/build-integrations/playwright/errors.mjs.map +1 -1
  18. package/dist/build-integrations/playwright/index.d.mts +2 -2
  19. package/dist/build-integrations/playwright/index.mjs +2 -2
  20. package/dist/build-integrations/playwright/wallet-context.d.mts +2 -1
  21. package/dist/build-integrations/playwright/wallet-context.mjs +141 -41
  22. package/dist/build-integrations/playwright/wallet-context.mjs.map +1 -1
  23. package/dist/build-integrations/runtime/dapp-kit-slot.mjs +1 -1
  24. package/dist/build-integrations/runtime/dapp-kit-slot.mjs.map +1 -1
  25. package/dist/build-integrations/vite/index.d.mts +82 -40
  26. package/dist/build-integrations/vite/index.mjs +281 -112
  27. package/dist/build-integrations/vite/index.mjs.map +1 -1
  28. package/dist/build-integrations/vitest/config.mjs +3 -4
  29. package/dist/build-integrations/vitest/config.mjs.map +1 -1
  30. package/dist/build-integrations/vitest/global-setup.mjs +8 -8
  31. package/dist/build-integrations/vitest/global-setup.mjs.map +1 -1
  32. package/dist/cli/doctor-probes.mjs +7 -2
  33. package/dist/cli/doctor-probes.mjs.map +1 -1
  34. package/dist/cli/main.mjs +5 -4
  35. package/dist/cli/main.mjs.map +1 -1
  36. package/dist/cli/wirings/apply.mjs +0 -1
  37. package/dist/cli/wirings/apply.mjs.map +1 -1
  38. package/dist/cli/wirings/codegen.mjs +37 -38
  39. package/dist/cli/wirings/codegen.mjs.map +1 -1
  40. package/dist/cli/wirings/dump-deployment.mjs +141 -0
  41. package/dist/cli/wirings/dump-deployment.mjs.map +1 -0
  42. package/dist/cli/wirings/snapshot.mjs +0 -1
  43. package/dist/cli/wirings/snapshot.mjs.map +1 -1
  44. package/dist/cli/wirings/up.mjs +7 -0
  45. package/dist/cli/wirings/up.mjs.map +1 -1
  46. package/dist/contracts/codegenable.d.mts +22 -25
  47. package/dist/contracts/codegenable.mjs +35 -4
  48. package/dist/contracts/codegenable.mjs.map +1 -1
  49. package/dist/contracts/config-bindings.mjs +43 -18
  50. package/dist/contracts/config-bindings.mjs.map +1 -1
  51. package/dist/index.mjs +1 -1
  52. package/dist/orchestrators/boot.d.mts +0 -10
  53. package/dist/orchestrators/boot.mjs +24 -54
  54. package/dist/orchestrators/boot.mjs.map +1 -1
  55. package/dist/orchestrators/codegen/bindings.mjs.map +1 -1
  56. package/dist/orchestrators/codegen/config-runtime.mjs +137 -112
  57. package/dist/orchestrators/codegen/config-runtime.mjs.map +1 -1
  58. package/dist/orchestrators/codegen/deployment-network-file.mjs +130 -0
  59. package/dist/orchestrators/codegen/deployment-network-file.mjs.map +1 -0
  60. package/dist/orchestrators/codegen/deployment-strict.mjs +77 -0
  61. package/dist/orchestrators/codegen/deployment-strict.mjs.map +1 -0
  62. package/dist/orchestrators/codegen/deployment.d.mts +63 -0
  63. package/dist/orchestrators/codegen/deployment.mjs +153 -0
  64. package/dist/orchestrators/codegen/deployment.mjs.map +1 -0
  65. package/dist/orchestrators/codegen/errors.mjs +7 -9
  66. package/dist/orchestrators/codegen/errors.mjs.map +1 -1
  67. package/dist/orchestrators/codegen/format.mjs +20 -1
  68. package/dist/orchestrators/codegen/format.mjs.map +1 -1
  69. package/dist/orchestrators/codegen/paths.mjs +1 -8
  70. package/dist/orchestrators/codegen/paths.mjs.map +1 -1
  71. package/dist/orchestrators/codegen/service.mjs +369 -141
  72. package/dist/orchestrators/codegen/service.mjs.map +1 -1
  73. package/dist/orchestrators/layers.mjs +0 -1
  74. package/dist/orchestrators/layers.mjs.map +1 -1
  75. package/dist/orchestrators/network-options.d.mts +37 -35
  76. package/dist/orchestrators/network-options.mjs +13 -10
  77. package/dist/orchestrators/network-options.mjs.map +1 -1
  78. package/dist/plugins/account/codegen.d.mts +3 -4
  79. package/dist/plugins/account/codegen.mjs +5 -7
  80. package/dist/plugins/account/codegen.mjs.map +1 -1
  81. package/dist/plugins/account/index.mjs +1 -3
  82. package/dist/plugins/account/index.mjs.map +1 -1
  83. package/dist/plugins/coin/codegen.mjs +5 -5
  84. package/dist/plugins/coin/codegen.mjs.map +1 -1
  85. package/dist/plugins/coin/index.mjs.map +1 -1
  86. package/dist/plugins/dashboard/domain.mjs +18 -1
  87. package/dist/plugins/dashboard/domain.mjs.map +1 -1
  88. package/dist/plugins/dashboard/schema/root.mjs +5 -3
  89. package/dist/plugins/dashboard/schema/root.mjs.map +1 -1
  90. package/dist/plugins/dashboard/schema/types.mjs +15 -2
  91. package/dist/plugins/dashboard/schema/types.mjs.map +1 -1
  92. package/dist/plugins/deepbook/codegen.mjs +4 -4
  93. package/dist/plugins/deepbook/codegen.mjs.map +1 -1
  94. package/dist/plugins/deepbook/index.d.mts +7 -3
  95. package/dist/plugins/deepbook/index.mjs +3 -3
  96. package/dist/plugins/deepbook/index.mjs.map +1 -1
  97. package/dist/plugins/internal/codegen-helpers.mjs +0 -1
  98. package/dist/plugins/internal/codegen-helpers.mjs.map +1 -1
  99. package/dist/plugins/package/codegen.d.mts +1 -12
  100. package/dist/plugins/package/codegen.mjs +43 -25
  101. package/dist/plugins/package/codegen.mjs.map +1 -1
  102. package/dist/plugins/package/dep-resolution.mjs +1 -1
  103. package/dist/plugins/package/dep-resolution.mjs.map +1 -1
  104. package/dist/plugins/package/index.d.mts +14 -11
  105. package/dist/plugins/package/index.mjs +6 -7
  106. package/dist/plugins/package/index.mjs.map +1 -1
  107. package/dist/plugins/seal/codegen.mjs +3 -3
  108. package/dist/plugins/seal/codegen.mjs.map +1 -1
  109. package/dist/plugins/seal/index.mjs.map +1 -1
  110. package/dist/plugins/sui/codegen.mjs +5 -4
  111. package/dist/plugins/sui/codegen.mjs.map +1 -1
  112. package/dist/plugins/sui/index.d.mts +10 -10
  113. package/dist/plugins/sui/index.mjs +1 -2
  114. package/dist/plugins/sui/index.mjs.map +1 -1
  115. package/dist/plugins/sui/move-summary-runner.mjs +29 -2
  116. package/dist/plugins/sui/move-summary-runner.mjs.map +1 -1
  117. package/dist/plugins/wallet/codegen.d.mts +8 -12
  118. package/dist/plugins/wallet/codegen.mjs +22 -15
  119. package/dist/plugins/wallet/codegen.mjs.map +1 -1
  120. package/dist/plugins/wallet/index.d.mts +4 -3
  121. package/dist/plugins/wallet/index.mjs +1 -1
  122. package/dist/plugins/wallet/index.mjs.map +1 -1
  123. package/dist/plugins/wallet/service.d.mts +2 -2
  124. package/dist/plugins/wallet/service.mjs +2 -3
  125. package/dist/plugins/wallet/service.mjs.map +1 -1
  126. package/dist/plugins/walrus/codegen.mjs +4 -4
  127. package/dist/plugins/walrus/codegen.mjs.map +1 -1
  128. package/dist/plugins/walrus/index.mjs +1 -1
  129. package/dist/plugins/walrus/index.mjs.map +1 -1
  130. package/dist/substrate/manifest.d.mts +17 -22
  131. package/dist/substrate/manifest.mjs +4 -6
  132. package/dist/substrate/manifest.mjs.map +1 -1
  133. package/dist/substrate/plugin-ctx.d.mts +6 -6
  134. package/dist/substrate/plugin-ctx.mjs.map +1 -1
  135. package/dist/substrate/runtime/cross-process/liveness.mjs.map +1 -1
  136. package/dist/substrate/runtime/index.mjs +1 -0
  137. package/dist/substrate/runtime/lifecycle/file-watcher.d.mts +1 -0
  138. package/dist/substrate/runtime/lifecycle/file-watcher.mjs +54 -0
  139. package/dist/substrate/runtime/lifecycle/file-watcher.mjs.map +1 -0
  140. package/dist/substrate/runtime/lifecycle/index.mjs +1 -0
  141. package/dist/substrate/runtime/lifecycle/watch-attribution.mjs +76 -6
  142. package/dist/substrate/runtime/lifecycle/watch-attribution.mjs.map +1 -1
  143. package/dist/substrate/runtime/manifest/manifest.mjs.map +1 -1
  144. package/dist/substrate/runtime/paths.mjs +1 -1
  145. package/dist/substrate/runtime/supervisor/start-supervisor.mjs +2 -6
  146. package/dist/substrate/runtime/supervisor/start-supervisor.mjs.map +1 -1
  147. package/dist/surfaces/cli/command-tree.mjs +6 -6
  148. package/dist/surfaces/cli/command-tree.mjs.map +1 -1
  149. package/dist/surfaces/cli/index.mjs +6 -4
  150. package/dist/surfaces/cli/index.mjs.map +1 -1
  151. package/dist/surfaces/tui/plain-renderer.mjs.map +1 -1
  152. package/package.json +1 -1
  153. package/dist/cli/wirings/dump-ids.mjs +0 -86
  154. package/dist/cli/wirings/dump-ids.mjs.map +0 -1
  155. package/dist/orchestrators/codegen/id-config.d.mts +0 -1
  156. package/dist/orchestrators/codegen/id-config.mjs +0 -107
  157. package/dist/orchestrators/codegen/id-config.mjs.map +0 -1
  158. package/dist/orchestrators/codegen/output-location.mjs +0 -32
  159. package/dist/orchestrators/codegen/output-location.mjs.map +0 -1
@@ -1,22 +1,20 @@
1
1
  import { defineSimpleConstExport } from "../internal/codegen-helpers.mjs";
2
2
  //#region src/plugins/account/codegen.ts
3
- /** Construct the Codegenable contribution. One emit per account.
3
+ /** Construct the Codegenable contribution. One decl per account.
4
4
  *
5
- * The emitter name is `account/${name}` (literal) so multiple
6
- * accounts in a single stack get co-located but typed-distinct
7
- * emissions; the codegen orchestrator can fold them into a single
8
- * `accounts` namespace at staging time. */
5
+ * Values-only: emits NO standalone file. The combined `accounts.ts`
6
+ * aggregate projection is consumed ONLY by boot's `assembleDeployment`,
7
+ * which folds it into the deployment envelope's network-agnostic
8
+ * `accounts` map (name address). No `accounts.ts` file is written. */
9
9
  const makeAccountCodegen = (parts) => defineSimpleConstExport({
10
10
  emitterName: `account/${parts.name}`,
11
11
  outputPath: `accounts/${parts.name}.ts`,
12
12
  exportName: parts.name,
13
13
  value: parts.resolved,
14
- outputLocation: "generated-extras",
15
14
  aggregateOnly: true,
16
15
  aggregate: {
17
16
  kind: "account",
18
17
  bucket: "accounts.ts",
19
- outputLocation: "generated-extras",
20
18
  project: (exported) => exported
21
19
  }
22
20
  });
@@ -1 +1 @@
1
- {"version":3,"file":"codegen.mjs","names":[],"sources":["../../../src/plugins/account/codegen.ts"],"sourcesContent":["// Account plugin — Codegenable contribution.\n//\n// Architecture §6 + 12-account.md \"Cross-component references\":\n// Codegen emits a constant address map keyed by account name so\n// downstream code can `import { accounts } from '<staged>/accounts'`\n// and read `accounts.alice.address` at the type level.\n//\n// Distilled-doc tension (12-account.md \"publicKey caveat\"):\n// impersonation accounts have a zero-buffer publicKey — emitting it\n// into the codegen bindings would be a type-level lie. The emitted\n// shape below carries the `source` discriminator INSTEAD of\n// publicKey; consumers branch on `source: 'impersonate'` rather than\n// publicKey-truthiness.\n//\n// SAFETY: the codegen output never emits secret material. Only the\n// `name`, `address`, `scheme`, and `source` fields land on disk.\n// `sensitive: false` is correct because none of these are secret.\n\nimport type { CodegenableDecl } from '../../contracts/codegenable.ts';\n\nimport { defineSimpleConstExport } from '../internal/codegen-helpers.ts';\n\n/** The typed shape the emitted file exports. Per-account record keyed\n * by name. */\nexport interface AccountBindings {\n\treadonly name: string;\n\treadonly address: string;\n\treadonly scheme: 'ed25519' | 'secp256k1' | 'secp256r1';\n\treadonly source: 'real' | 'impersonate';\n}\n\n/** Construct the Codegenable contribution. One emit per account.\n *\n * The emitter name is `account/${name}` (literal) so multiple\n * accounts in a single stack get co-located but typed-distinct\n * emissions; the codegen orchestrator can fold them into a single\n * `accounts` namespace at staging time. */\nexport const makeAccountCodegen = <Name extends string>(parts: {\n\treadonly name: Name;\n\treadonly resolved: AccountBindings;\n}): CodegenableDecl<`account/${Name}`> =>\n\tdefineSimpleConstExport({\n\t\temitterName: `account/${parts.name}` as `account/${Name}`,\n\t\toutputPath: `accounts/${parts.name}.ts`,\n\t\texportName: parts.name,\n\t\tvalue: parts.resolved,\n\t\t// Dev-only surface: the combined `accounts.ts` lands in the\n\t\t// gitignored `generated-extras` tree (reached via `@devstack-dev`),\n\t\t// and no standalone per-account file is emitted (aggregateOnly).\n\t\t// The template app must NOT use accounts at runtime; examples\n\t\t// (dev-only) import them from `@devstack-dev`.\n\t\toutputLocation: 'generated-extras',\n\t\taggregateOnly: true,\n\t\taggregate: {\n\t\t\tkind: 'account',\n\t\t\tbucket: 'accounts.ts',\n\t\t\toutputLocation: 'generated-extras',\n\t\t\t// Pass-through: this decl's exported map already keys by\n\t\t\t// account name, which is the aggregate's merge key.\n\t\t\tproject: (exported) => exported,\n\t\t},\n\t});\n"],"mappings":";;;;;;;;AAqCA,MAAa,sBAA2C,UAIvD,wBAAwB;CACvB,aAAa,WAAW,MAAM;CAC9B,YAAY,YAAY,MAAM,KAAK;CACnC,YAAY,MAAM;CAClB,OAAO,MAAM;CAMb,gBAAgB;CAChB,eAAe;CACf,WAAW;EACV,MAAM;EACN,QAAQ;EACR,gBAAgB;EAGhB,UAAU,aAAa;CACxB;AACD,CAAC"}
1
+ {"version":3,"file":"codegen.mjs","names":[],"sources":["../../../src/plugins/account/codegen.ts"],"sourcesContent":["// Account plugin — Codegenable contribution.\n//\n// Architecture §6 + 12-account.md \"Cross-component references\":\n// Accounts are a DEV-only, network-AGNOSTIC concept. The account name →\n// address map rides the deployment ENVELOPE's `accounts` channel: boot's\n// `assembleDeployment` folds this decl's `accounts.ts` aggregate\n// projection into `deployment.accounts`, which the generated\n// `config-runtime.ts` exposes via `resolveAccounts()`. The decl is\n// VALUES-ONLY it emits NO standalone file and lands no `accounts.ts`\n// tree on disk (the dev wallet reads the addresses off the injected\n// deployment, not an import).\n//\n// SAFETY: only the non-secret `name` + `address` reach the deployment.\n// No keypair / secret material is ever projected.\n\nimport type { CodegenableDecl } from '../../contracts/codegenable.ts';\n\nimport { defineSimpleConstExport } from '../internal/codegen-helpers.ts';\n\n/** The typed shape the account decl projects. Per-account record keyed\n * by name → address. (No `scheme`/`source` — those had no consumer and\n * the address is the only field the envelope-level `accounts` map carries.) */\nexport interface AccountBindings {\n\treadonly name: string;\n\treadonly address: string;\n}\n\n/** Construct the Codegenable contribution. One decl per account.\n *\n * Values-only: emits NO standalone file. The combined `accounts.ts`\n * aggregate projection is consumed ONLY by boot's `assembleDeployment`,\n * which folds it into the deployment envelope's network-agnostic\n * `accounts` map (name address). No `accounts.ts` file is written. */\nexport const makeAccountCodegen = <Name extends string>(parts: {\n\treadonly name: Name;\n\treadonly resolved: AccountBindings;\n}): CodegenableDecl<`account/${Name}`> =>\n\tdefineSimpleConstExport({\n\t\temitterName: `account/${parts.name}` as `account/${Name}`,\n\t\toutputPath: `accounts/${parts.name}.ts`,\n\t\texportName: parts.name,\n\t\tvalue: parts.resolved,\n\t\taggregateOnly: true,\n\t\taggregate: {\n\t\t\tkind: 'account',\n\t\t\tbucket: 'accounts.ts',\n\t\t\t// Pass-through: this decl's exported map already keys by\n\t\t\t// account name, which is the aggregate's merge key.\n\t\t\tproject: (exported) => exported,\n\t\t},\n\t});\n"],"mappings":";;;;;;;;AAiCA,MAAa,sBAA2C,UAIvD,wBAAwB;CACvB,aAAa,WAAW,MAAM;CAC9B,YAAY,YAAY,MAAM,KAAK;CACnC,YAAY,MAAM;CAClB,OAAO,MAAM;CACb,eAAe;CACf,WAAW;EACV,MAAM;EACN,QAAQ;EAGR,UAAU,aAAa;CACxB;AACD,CAAC"}
@@ -170,9 +170,7 @@ const account = (name, opts) => {
170
170
  name,
171
171
  resolved: {
172
172
  name,
173
- address: resolved.address,
174
- scheme: resolved.scheme,
175
- source: resolved.source
173
+ address: resolved.address
176
174
  }
177
175
  }));
178
176
  ctx.provides(makeAccountRegistryContribution(realEntry));
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/plugins/account/index.ts"],"sourcesContent":["// Account plugin — barrel + `account(name, opts?)` factory.\n//\n// Architecture (12-account.md): Account is the named-identity layer\n// for devstack. It acquires a keypair (or impersonation slot), funds\n// it (default + cross-cutting), registers `{name, address}`, and\n// publishes a per-account resolved value via a unique resource id.\n//\n// User-facing factory shape — THREE variants:\n//\n// account('alice') // default ephemeral\n// account('alice', { kind: 'ephemeral', funding: [{ coin: 'sui', amount: 5_000_000_000n }] })\n// account('alice', { kind: 'signer', signer: hardwareWallet })\n// account('alice', { kind: 'impersonate', address: '0xabc...' }) // fork-mode only\n//\n// The `signer` variant is the single bring-your-own door. Pass any\n// `@mysten/sui/cryptography` `Signer` or `Keypair`. Loading a secret\n// from an env var or an inline literal is expressed by constructing\n// the keypair yourself and passing it as the signer:\n//\n// account('ci', { kind: 'signer', signer: Ed25519Keypair.fromSecretKey(process.env.ALICE_PRIVATE_KEY!) })\n// account('demo', { kind: 'signer', signer: Ed25519Keypair.fromSecretKey('suiprivkey1...') })\n//\n// **Bare-form default**: `account('alice')` is shorthand for\n//\n// { kind: 'ephemeral' } plus default SUI funding.\n//\n// Distilled-doc invariant (12-account.md \"Make the bare-form auto-\n// promotion to fork-impersonate funding discoverable\"): on fork-\n// runtime Sui, the default-funding pass internally promotes from\n// \"faucet POST\" to \"pay-from-seed-via-impersonate\" because no\n// faucet exists on a fork. The promotion is LOUD by default — we\n// emit a `log.appended` event the first time the promotion fires\n// (see `funding.ts`).\n//\n// During `start`, the plugin emits (via the typed `ctx.*` verbs) plus an\n// error-tag contribution:\n//\n// 1. `ctx.snapshotExtra` — secret-material subtree (ephemeral only).\n// 2. `ctx.codegen` — `account-map` bindings (name → address).\n// 3. `ctx.provides` — per-stack `account:<name>` registry entry.\n\nimport { Effect } from 'effect';\n\nimport {\n\tdefinePlugin,\n\tresource,\n\tstaticInputIdentity,\n\ttype AnyResourceRef,\n} from '../../api/define-plugin.ts';\nimport { PluginContext } from '../../substrate/plugin-ctx.ts';\nimport { IdentityContext, StackPathsService } from '../../substrate/runtime/paths.ts';\nimport { suiResource, SuiLogAttr } from '../sui/index.ts';\n\nimport { makeAccountCodegen } from './codegen.ts';\nimport { accountAcquireError, type AccountAcquireError } from './errors.ts';\nimport {\n\tDEFAULT_EPHEMERAL_FUND_MIST,\n\tSUI_FULL_COIN_TYPE,\n\ttype AccountFunding,\n\ttype AccountFundingEntry,\n\ttype AccountFundingResult,\n\ttype CrossCuttingFundingEntry,\n\ttype CrossCuttingFundingProvider,\n\ttype CoinMember,\n\ttype ProjectedFundingEntry,\n} from './funding.ts';\nimport {\n\tmakeAccountProjectionContribution,\n\tmakeAccountRegistryContribution,\n\ttype AccountRegistryEntry,\n\ttype AccountRegistryFunding,\n} from './registry.ts';\nimport { makeAccountSnapshotable } from './snapshot.ts';\nimport {\n\tacquireAccount,\n\tassertAccountName,\n\ttype AccountAcquireContext,\n\ttype AccountOptions,\n\ttype ResolvedAccountOptions,\n\ttype AccountValue,\n} from './service.ts';\n\n// ---------------------------------------------------------------------------\n// Resource construction\n// ---------------------------------------------------------------------------\n\n/** Per-account-instance resource. The substrate's resource id MUST be unique\n * across the stack — we encode the account name as a literal-typed\n * template literal so the compiler catches duplicate-name composes\n * at the `defineDevstack` call site.\n *\n * Distilled-doc invariant: resource id flows into the on-disk path, the\n * manifest key, container labels, and generated TypeScript exports —\n * the identifier-safe name validation (in `service.ts`) protects\n * those call sites. */\nexport type AccountResourceId<Name extends string> = `account/${Name}`;\n\nconst accountResource = <Name extends string>(name: Name) =>\n\tresource<AccountResourceId<Name>, AccountValue>(`account/${name}` as AccountResourceId<Name>);\n\n// ---------------------------------------------------------------------------\n// User-facing factory\n// ---------------------------------------------------------------------------\n\n/** Construct the account plugin instance.\n *\n * Variants — typed at the API boundary via the discriminated\n * `AccountOptions` union. The factory's second arg is OPTIONAL —\n * omitting it yields the bare-form default (ephemeral + default\n * funding). Passing a non-matching `kind:` is a compile error\n * thanks to the union's discriminator.\n *\n * Distilled-doc invariant: the name is validated at the FACTORY\n * boundary (not just at acquire time) so a typo surfaces\n * immediately, not at runtime. */\ntype FundingCoinDependencies<Entries extends readonly unknown[]> = Entries extends readonly [\n\tinfer Head,\n\t...infer Tail,\n]\n\t? Head extends { readonly coin: infer Coin }\n\t\t? Coin extends CoinMember\n\t\t\t? readonly [Coin, ...FundingCoinDependencies<Tail>]\n\t\t\t: FundingCoinDependencies<Tail>\n\t\t: FundingCoinDependencies<Tail>\n\t: readonly [];\n\ntype FundingProviderDependenciesFor<Provider> = Provider extends readonly AnyResourceRef[]\n\t? Provider\n\t: Provider extends AnyResourceRef\n\t\t? readonly [Provider]\n\t\t: readonly [];\n\ntype FundingEntryProviderDependencies<Entry> = Entry extends { readonly coin: CoinMember }\n\t? 'via' extends keyof Entry\n\t\t? Entry extends { readonly via: infer Provider }\n\t\t\t? FundingProviderDependenciesFor<Provider>\n\t\t\t: readonly []\n\t\t: readonly []\n\t: readonly [];\n\ntype FundingProviderDependencies<Entries extends readonly unknown[]> = Entries extends readonly [\n\tinfer Head,\n\t...infer Tail,\n]\n\t? readonly [...FundingEntryProviderDependencies<Head>, ...FundingProviderDependencies<Tail>]\n\t: readonly [];\n\ntype AccountDependencyMembers<Funding extends AccountFunding> = readonly [\n\ttypeof suiResource,\n\t...FundingCoinDependencies<Funding>,\n\t...FundingProviderDependencies<Funding>,\n];\n\nconst isCoinFundingEntry = (entry: AccountFundingEntry): entry is CrossCuttingFundingEntry =>\n\ttypeof entry === 'object' && entry.coin !== 'sui';\n\nconst fundingProviders = (\n\tprovider: CrossCuttingFundingProvider | undefined,\n): ReadonlyArray<AnyResourceRef> => {\n\tif (provider === undefined) return [];\n\treturn Array.isArray(provider)\n\t\t? (provider as ReadonlyArray<AnyResourceRef>)\n\t\t: [provider as AnyResourceRef];\n};\n\n/** Validate a SUI funding amount and project it to bigint MIST.\n * Returns a typed-error Effect so a bad amount surfaces as\n * `AccountAcquireError(phase: 'validate-funding')` on the failure\n * channel — NOT a synchronous throw lifted to a defect when called\n * from inside the acquire body's `Effect.gen`. */\nconst fundingAmountToBigInt = (\n\tamount: number | bigint,\n\taccountName: string,\n): Effect.Effect<bigint, AccountAcquireError> => {\n\tif (typeof amount === 'bigint') {\n\t\tif (amount < 0n) {\n\t\t\treturn Effect.fail(\n\t\t\t\taccountAcquireError({\n\t\t\t\t\tphase: 'validate-funding',\n\t\t\t\t\taccountName,\n\t\t\t\t\tvariant: 'ephemeral',\n\t\t\t\t\tmessage: `Account '${accountName}': SUI funding amount must be a non-negative integer in MIST (got ${amount}).`,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\treturn Effect.succeed(amount);\n\t}\n\tif (!Number.isSafeInteger(amount) || amount < 0) {\n\t\treturn Effect.fail(\n\t\t\taccountAcquireError({\n\t\t\t\tphase: 'validate-funding',\n\t\t\t\taccountName,\n\t\t\t\tvariant: 'ephemeral',\n\t\t\t\tmessage: `Account '${accountName}': SUI funding amount must be a non-negative safe integer in MIST (got ${amount}).`,\n\t\t\t}),\n\t\t);\n\t}\n\treturn Effect.succeed(BigInt(amount));\n};\n\nconst coinLabelFor = (coin: { readonly fullCoinType: string; readonly symbol?: string }): string =>\n\tcoin.symbol ?? coin.fullCoinType.split('::').at(-1) ?? coin.fullCoinType;\n\nconst inputAmount = (amount: number | bigint): string => amount.toString();\n\nconst inputFundingProviderIds = (\n\tprovider: CrossCuttingFundingProvider | undefined,\n): ReadonlyArray<string> =>\n\tfundingProviders(provider)\n\t\t.map((ref) => ref.id)\n\t\t.sort((a, b) => a.localeCompare(b));\n\nconst inputFundingIdentityFor = (entry: AccountFundingEntry): unknown => {\n\tif (entry.coin === 'sui') {\n\t\treturn { coin: 'sui', amountMist: inputAmount(entry.amount) };\n\t}\n\tconst via = inputFundingProviderIds(entry.via);\n\treturn {\n\t\tcoin: entry.coin.id,\n\t\tamountMist: entry.amount.toString(),\n\t\t...(via.length === 0 ? {} : { via }),\n\t};\n};\n\nconst accountFundingInputIdentity = (\n\topts: ResolvedAccountOptions,\n\tfundingEntries: AccountFunding,\n): ReadonlyArray<unknown> => {\n\tconst explicit = fundingEntries.map(inputFundingIdentityFor);\n\tif (opts.kind === 'ephemeral' && opts.funding === undefined) {\n\t\treturn [{ coin: 'sui', amountMist: DEFAULT_EPHEMERAL_FUND_MIST.toString() }, ...explicit];\n\t}\n\treturn explicit;\n};\n\nconst accountVariantInputIdentity = (opts: ResolvedAccountOptions): unknown => {\n\tswitch (opts.kind) {\n\t\tcase 'ephemeral':\n\t\t\treturn { kind: 'ephemeral' };\n\t\tcase 'impersonate':\n\t\t\treturn { kind: 'impersonate', address: opts.address };\n\t\tcase 'signer':\n\t\t\treturn {\n\t\t\t\tkind: 'signer',\n\t\t\t\taddress: opts.addressOverride ?? opts.signer.toSuiAddress(),\n\t\t\t};\n\t}\n};\n\nconst accountInputIdentity = (\n\topts: ResolvedAccountOptions,\n\tfundingEntries: AccountFunding,\n): unknown => ({\n\tplugin: 'account',\n\tname: opts.name,\n\tvariant: accountVariantInputIdentity(opts),\n\tfunding: accountFundingInputIdentity(opts, fundingEntries),\n});\n\n// ---------------------------------------------------------------------------\n// Capability emission — dynamic (POST-acquire). Receives the resolved\n// `AccountValue` + the identity so snapshot + codegen + registry + projection\n// decls reference the REAL address (and identity app/stack) — the static form\n// would force placeholder values for fields only known at acquire time.\n//\n// The account `start` emits its four contributions inline — snapshot →\n// codegen → registry → projection — directly via the typed `ctx` verbs,\n// AFTER resolving the value. The decl shapes + emit ORDER are load-bearing.\n// ---------------------------------------------------------------------------\n\nexport const account = <const N extends string, const Funding extends AccountFunding = readonly []>(\n\tname: N,\n\topts?: AccountOptions<Funding>,\n) => {\n\tassertAccountName(name);\n\n\t// Normalize bare-form to ephemeral default. The user-facing\n\t// `AccountOptions` union does not include \"kind absent\" — we\n\t// inject the default here so the rest of the body sees a\n\t// fully-discriminated value.\n\tconst opts2: ResolvedAccountOptions =\n\t\topts === undefined\n\t\t\t? { kind: 'ephemeral', name }\n\t\t\t: ({ ...opts, name } as ResolvedAccountOptions);\n\n\tconst accountRef = accountResource(name);\n\n\t// Pull the funding resource tuple out of opts (may be undefined for\n\t// the bare form / variants without funding). The tuple preserves\n\t// plugin-valued refs when the caller passed coin plugins, so\n\t// recursive stack composition can include those publishers.\n\tconst fundingEntries = (opts?.funding ?? []) as unknown as Funding;\n\tconst fundingEntryList = fundingEntries as AccountFunding;\n\tconst coinFundingEntries = fundingEntryList.filter(isCoinFundingEntry);\n\tconst fundingMembers = coinFundingEntries.map((e) => e.coin);\n\tconst strategyProviderMembers = coinFundingEntries.flatMap((entry) =>\n\t\tfundingProviders(entry.via),\n\t);\n\tconst dependencies = [\n\t\tsuiResource,\n\t\t...fundingMembers,\n\t\t...strategyProviderMembers,\n\t] as unknown as AccountDependencyMembers<Funding>;\n\n\treturn definePlugin({\n\t\tid: accountRef.id,\n\t\tdependsOn: dependencies,\n\t\t// Account is a value-producer (no long-lived server / container);\n\t\t// tasks acquire their value, publish contributions, then reach\n\t\t// `done`.\n\t\trole: 'task',\n\t\tsection: 'account',\n\t\tinputIdentity: staticInputIdentity(accountInputIdentity(opts2, fundingEntryList)),\n\t\t// `deps` auto-infers from the resolved `dependsOn`; `ctx` arrives\n\t\t// via the `PluginContext` service.\n\t\tstart: (deps) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst ctx = yield* PluginContext;\n\t\t\t\tconst [sui, ...resolvedDeps] = deps;\n\t\t\t\tconst resolvedCoinValues = resolvedDeps.slice(\n\t\t\t\t\t0,\n\t\t\t\t\tcoinFundingEntries.length,\n\t\t\t\t) as ReadonlyArray<{\n\t\t\t\t\treadonly fullCoinType: string;\n\t\t\t\t\treadonly symbol?: string;\n\t\t\t\t}>;\n\t\t\t\t// Identity + on-disk runtime root come from the\n\t\t\t\t// supervisor-provided substrate context.\n\t\t\t\tconst identity = yield* IdentityContext;\n\t\t\t\tconst paths = yield* StackPathsService;\n\n\t\t\t\t// Project each funding dependency value to a\n\t\t\t\t// `{fullCoinType, amount}` projection. Dependency order\n\t\t\t\t// mirrors `fundingEntries`, after the hard Sui upstream.\n\t\t\t\t// SUI amount validation lives on the failure channel so a\n\t\t\t\t// negative / non-integer amount surfaces as a typed\n\t\t\t\t// `AccountAcquireError(phase: 'validate-funding')` instead\n\t\t\t\t// of a defect (the old sync-throw lifted to a defect when\n\t\t\t\t// run inside this `Effect.gen`).\n\t\t\t\tlet coinIndex = 0;\n\t\t\t\tconst projectedFundingMutable: ProjectedFundingEntry[] = [];\n\t\t\t\tfor (const entry of fundingEntryList) {\n\t\t\t\t\tif (entry.coin === 'sui') {\n\t\t\t\t\t\tconst amount = yield* fundingAmountToBigInt(entry.amount, name);\n\t\t\t\t\t\tprojectedFundingMutable.push({\n\t\t\t\t\t\t\tcoin: 'SUI',\n\t\t\t\t\t\t\tfullCoinType: SUI_FULL_COIN_TYPE,\n\t\t\t\t\t\t\tamount,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tconst resolvedCoin = resolvedCoinValues[coinIndex]!;\n\t\t\t\t\tcoinIndex += 1;\n\t\t\t\t\tprojectedFundingMutable.push({\n\t\t\t\t\t\tcoin: coinLabelFor(resolvedCoin),\n\t\t\t\t\t\tfullCoinType: resolvedCoin.fullCoinType,\n\t\t\t\t\t\tamount: entry.amount,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tconst projectedFunding: ReadonlyArray<ProjectedFundingEntry> = projectedFundingMutable;\n\n\t\t\t\tconst acquireCtx: AccountAcquireContext = {\n\t\t\t\t\tsui: {\n\t\t\t\t\t\tmode: sui.mode,\n\t\t\t\t\t\tchainId: sui.chainId,\n\t\t\t\t\t\tsdk: sui.sdk,\n\t\t\t\t\t\tfork: sui.fork,\n\t\t\t\t\t},\n\t\t\t\t\truntimeRoot: paths.stackRoot,\n\t\t\t\t\tapp: identity.app,\n\t\t\t\t\tstack: identity.stack,\n\t\t\t\t\temitAutoPromotionEvent: () =>\n\t\t\t\t\t\tEffect.logWarning('account funding auto-promoted for fork mode').pipe(\n\t\t\t\t\t\t\tEffect.annotateLogs({\n\t\t\t\t\t\t\t\t'account.name': name,\n\t\t\t\t\t\t\t\t'account.funding.from': 'faucet',\n\t\t\t\t\t\t\t\t'account.funding.to': 'pay-from-seed-via-impersonate',\n\t\t\t\t\t\t\t\t[SuiLogAttr.mode]: 'fork',\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t),\n\t\t\t\t\tprojectedFunding,\n\t\t\t\t};\n\t\t\t\tconst resolved = yield* acquireAccount(opts2, acquireCtx);\n\n\t\t\t\t// Emit the resolved account's contributions inline, in order:\n\t\t\t\t// snapshot → codegen → registry → projection. `resolved` is the\n\t\t\t\t// just-acquired `AccountValue`; `identity` (from\n\t\t\t\t// `IdentityContext`) supplies the snapshot's pre-restore guard\n\t\t\t\t// app/stack; the variant kind is `opts2.kind`. The registry +\n\t\t\t\t// projection decls share one `realEntry` so the registered\n\t\t\t\t// strategy and the published event agree byte-for-byte.\n\t\t\t\tconst realEntry: AccountRegistryEntry = {\n\t\t\t\t\tname,\n\t\t\t\t\taddress: resolved.address,\n\t\t\t\t\tscheme: resolved.scheme,\n\t\t\t\t\tsource: resolved.source,\n\t\t\t\t\tfunding: fundingProjectionForResult(resolved.funding),\n\t\t\t\t};\n\t\t\t\tctx.snapshotExtra(\n\t\t\t\t\tmakeAccountSnapshotable({\n\t\t\t\t\t\taccountName: name,\n\t\t\t\t\t\tvariant: opts2.kind,\n\t\t\t\t\t\tapp: identity.app,\n\t\t\t\t\t\tstack: identity.stack,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\tctx.codegen(\n\t\t\t\t\tmakeAccountCodegen<N>({\n\t\t\t\t\t\tname,\n\t\t\t\t\t\tresolved: {\n\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\taddress: resolved.address,\n\t\t\t\t\t\t\tscheme: resolved.scheme,\n\t\t\t\t\t\t\tsource: resolved.source,\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\tctx.provides(\n\t\t\t\t\tmakeAccountRegistryContribution<N>(\n\t\t\t\t\t\trealEntry as AccountRegistryEntry & { readonly name: N },\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\tctx.publish(\n\t\t\t\t\tmakeAccountProjectionContribution<N>(\n\t\t\t\t\t\trealEntry as AccountRegistryEntry & { readonly name: N },\n\t\t\t\t\t),\n\t\t\t\t);\n\n\t\t\t\treturn resolved;\n\t\t\t}),\n\t});\n};\n\n/** @internal — exported only for the projection regression test\n * (`test/plugins/account/funding-projection.test.ts`). Projects the runtime\n * `AccountFundingResult` (requested + applied) into the registry's\n * per-row funded/already-satisfied/skipped status. */\nexport const fundingProjectionForResult = (\n\tfunding: AccountFundingResult,\n): AccountRegistryFunding => {\n\tif (funding.requested.length === 0) {\n\t\treturn { status: 'skipped', balanceMist: null, requestedMist: null, entries: [] };\n\t}\n\n\t// Positional outcome consume so the projection can distinguish a\n\t// real faucet call (`funded`) from the pre-existing-balance\n\t// short-circuit (`already-satisfied`). `applied` is an\n\t// order-preserving SUBSEQUENCE of `requested`:\n\t// `applyCrossCuttingFunding` (and the default-funding pass) iterate\n\t// `requested` in order and only push a processed entry onto\n\t// `applied`, dropping zero-amount and no-strategy entries. So we\n\t// walk a cursor through `applied` and adopt its outcome for the\n\t// matching requested row by (fullCoinType, amount) — a keyed Map\n\t// would collapse duplicate (coin, amount) entries and mislabel the\n\t// genuinely-funded first row as `already-satisfied`. An entry the\n\t// funding pass never reached (zero-amount, or absent from `applied`)\n\t// does not match the cursor and projects as `skipped` without\n\t// advancing it.\n\tlet appliedCursor = 0;\n\tconst entries = funding.requested.map((entry) => {\n\t\tconst candidate = funding.applied[appliedCursor];\n\t\tconst matches =\n\t\t\tcandidate !== undefined &&\n\t\t\tcandidate.fullCoinType === entry.fullCoinType &&\n\t\t\tcandidate.amount === entry.amount;\n\t\tconst outcome = matches ? candidate.outcome : undefined;\n\t\tif (matches) {\n\t\t\tappliedCursor += 1;\n\t\t}\n\t\treturn {\n\t\t\tcoin: entry.coin,\n\t\t\tfullCoinType: entry.fullCoinType,\n\t\t\tamount: entry.amount.toString(),\n\t\t\tstatus:\n\t\t\t\toutcome === 'funded'\n\t\t\t\t\t? ('funded' as const)\n\t\t\t\t\t: outcome === 'already-satisfied'\n\t\t\t\t\t\t? ('already-satisfied' as const)\n\t\t\t\t\t\t: ('skipped' as const),\n\t\t};\n\t});\n\tconst requestedMist =\n\t\tfunding.requested\n\t\t\t.find((entry) => entry.fullCoinType === SUI_FULL_COIN_TYPE)\n\t\t\t?.amount.toString() ?? null;\n\tconst settledCount = entries.filter(\n\t\t(entry) => entry.status === 'funded' || entry.status === 'already-satisfied',\n\t).length;\n\treturn {\n\t\t// `funded` here means \"every requested entry is satisfied\" —\n\t\t// either the faucet succeeded or the pre-existing balance\n\t\t// already covered it. `unknown` covers the mixed partial-success\n\t\t// case so consumers can distinguish from \"nothing ran at all\".\n\t\tstatus: settledCount === entries.length ? 'funded' : settledCount === 0 ? 'skipped' : 'unknown',\n\t\tbalanceMist: null,\n\t\trequestedMist,\n\t\tentries,\n\t};\n};\n\n// ---------------------------------------------------------------------------\n// Re-exports for advanced callers (Coin, Wallet, Package)\n// ---------------------------------------------------------------------------\n\nexport type {\n\tAccountOptions,\n\tAccountSignerInput,\n\tResolvedAccountOptions,\n\tAccountValue,\n\tSignAndExecuteResult,\n\tTxResult,\n} from './service.ts';\nexport type {\n\tAccountError,\n\tAccountAcquireError,\n\tAccountAcquirePhase,\n\tAccountSignError,\n\tAccountSignPhase,\n\tAccountVariantKind,\n} from './errors.ts';\nexport type {\n\tAccountFunding,\n\tAccountFundingEntry,\n\tAccountFundingCoinValue,\n\tAccountFundingResult,\n\tAccountFundingRequest,\n\tAccountFundingStrategy,\n\tAppliedFunding,\n\tAppliedFundingEntry,\n\tAppliedFundingOutcome,\n\tCoinMember,\n\tCrossCuttingFundingEntry,\n\tCrossCuttingFundingProvider,\n\tFundEphemeralDefaultOutcome,\n\tProjectedFunding,\n\tProjectedFundingEntry,\n\tSuiFundingEntry,\n} from './funding.ts';\nexport { DEFAULT_EPHEMERAL_FUND_MIST, SUI_FULL_COIN_TYPE } from './funding.ts';\nexport type { AccountBindings } from './codegen.ts';\nexport type {\n\tAccountRegistryEntry,\n\tAccountRegistryFunding,\n\tAccountRegistryKey,\n} from './registry.ts';\nexport { accountRegistryKey } from './registry.ts';\nexport type { SyntheticImpersonationSigner } from './variants/impersonate.ts';\nexport type { SignatureScheme, ResolvedKeypair } from './keypair.ts';\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/plugins/account/index.ts"],"sourcesContent":["// Account plugin — barrel + `account(name, opts?)` factory.\n//\n// Architecture (12-account.md): Account is the named-identity layer\n// for devstack. It acquires a keypair (or impersonation slot), funds\n// it (default + cross-cutting), registers `{name, address}`, and\n// publishes a per-account resolved value via a unique resource id.\n//\n// User-facing factory shape — THREE variants:\n//\n// account('alice') // default ephemeral\n// account('alice', { kind: 'ephemeral', funding: [{ coin: 'sui', amount: 5_000_000_000n }] })\n// account('alice', { kind: 'signer', signer: hardwareWallet })\n// account('alice', { kind: 'impersonate', address: '0xabc...' }) // fork-mode only\n//\n// The `signer` variant is the single bring-your-own door. Pass any\n// `@mysten/sui/cryptography` `Signer` or `Keypair`. Loading a secret\n// from an env var or an inline literal is expressed by constructing\n// the keypair yourself and passing it as the signer:\n//\n// account('ci', { kind: 'signer', signer: Ed25519Keypair.fromSecretKey(process.env.ALICE_PRIVATE_KEY!) })\n// account('demo', { kind: 'signer', signer: Ed25519Keypair.fromSecretKey('suiprivkey1...') })\n//\n// **Bare-form default**: `account('alice')` is shorthand for\n//\n// { kind: 'ephemeral' } plus default SUI funding.\n//\n// Distilled-doc invariant (12-account.md \"Make the bare-form auto-\n// promotion to fork-impersonate funding discoverable\"): on fork-\n// runtime Sui, the default-funding pass internally promotes from\n// \"faucet POST\" to \"pay-from-seed-via-impersonate\" because no\n// faucet exists on a fork. The promotion is LOUD by default — we\n// emit a `log.appended` event the first time the promotion fires\n// (see `funding.ts`).\n//\n// During `start`, the plugin emits (via the typed `ctx.*` verbs) plus an\n// error-tag contribution:\n//\n// 1. `ctx.snapshotExtra` — secret-material subtree (ephemeral only).\n// 2. `ctx.codegen` — `account-map` bindings (name → address).\n// 3. `ctx.provides` — per-stack `account:<name>` registry entry.\n\nimport { Effect } from 'effect';\n\nimport {\n\tdefinePlugin,\n\tresource,\n\tstaticInputIdentity,\n\ttype AnyResourceRef,\n} from '../../api/define-plugin.ts';\nimport { PluginContext } from '../../substrate/plugin-ctx.ts';\nimport { IdentityContext, StackPathsService } from '../../substrate/runtime/paths.ts';\nimport { suiResource, SuiLogAttr } from '../sui/index.ts';\n\nimport { makeAccountCodegen } from './codegen.ts';\nimport { accountAcquireError, type AccountAcquireError } from './errors.ts';\nimport {\n\tDEFAULT_EPHEMERAL_FUND_MIST,\n\tSUI_FULL_COIN_TYPE,\n\ttype AccountFunding,\n\ttype AccountFundingEntry,\n\ttype AccountFundingResult,\n\ttype CrossCuttingFundingEntry,\n\ttype CrossCuttingFundingProvider,\n\ttype CoinMember,\n\ttype ProjectedFundingEntry,\n} from './funding.ts';\nimport {\n\tmakeAccountProjectionContribution,\n\tmakeAccountRegistryContribution,\n\ttype AccountRegistryEntry,\n\ttype AccountRegistryFunding,\n} from './registry.ts';\nimport { makeAccountSnapshotable } from './snapshot.ts';\nimport {\n\tacquireAccount,\n\tassertAccountName,\n\ttype AccountAcquireContext,\n\ttype AccountOptions,\n\ttype ResolvedAccountOptions,\n\ttype AccountValue,\n} from './service.ts';\n\n// ---------------------------------------------------------------------------\n// Resource construction\n// ---------------------------------------------------------------------------\n\n/** Per-account-instance resource. The substrate's resource id MUST be unique\n * across the stack — we encode the account name as a literal-typed\n * template literal so the compiler catches duplicate-name composes\n * at the `defineDevstack` call site.\n *\n * Distilled-doc invariant: resource id flows into the on-disk path, the\n * manifest key, container labels, and generated TypeScript exports —\n * the identifier-safe name validation (in `service.ts`) protects\n * those call sites. */\nexport type AccountResourceId<Name extends string> = `account/${Name}`;\n\nconst accountResource = <Name extends string>(name: Name) =>\n\tresource<AccountResourceId<Name>, AccountValue>(`account/${name}` as AccountResourceId<Name>);\n\n// ---------------------------------------------------------------------------\n// User-facing factory\n// ---------------------------------------------------------------------------\n\n/** Construct the account plugin instance.\n *\n * Variants — typed at the API boundary via the discriminated\n * `AccountOptions` union. The factory's second arg is OPTIONAL —\n * omitting it yields the bare-form default (ephemeral + default\n * funding). Passing a non-matching `kind:` is a compile error\n * thanks to the union's discriminator.\n *\n * Distilled-doc invariant: the name is validated at the FACTORY\n * boundary (not just at acquire time) so a typo surfaces\n * immediately, not at runtime. */\ntype FundingCoinDependencies<Entries extends readonly unknown[]> = Entries extends readonly [\n\tinfer Head,\n\t...infer Tail,\n]\n\t? Head extends { readonly coin: infer Coin }\n\t\t? Coin extends CoinMember\n\t\t\t? readonly [Coin, ...FundingCoinDependencies<Tail>]\n\t\t\t: FundingCoinDependencies<Tail>\n\t\t: FundingCoinDependencies<Tail>\n\t: readonly [];\n\ntype FundingProviderDependenciesFor<Provider> = Provider extends readonly AnyResourceRef[]\n\t? Provider\n\t: Provider extends AnyResourceRef\n\t\t? readonly [Provider]\n\t\t: readonly [];\n\ntype FundingEntryProviderDependencies<Entry> = Entry extends { readonly coin: CoinMember }\n\t? 'via' extends keyof Entry\n\t\t? Entry extends { readonly via: infer Provider }\n\t\t\t? FundingProviderDependenciesFor<Provider>\n\t\t\t: readonly []\n\t\t: readonly []\n\t: readonly [];\n\ntype FundingProviderDependencies<Entries extends readonly unknown[]> = Entries extends readonly [\n\tinfer Head,\n\t...infer Tail,\n]\n\t? readonly [...FundingEntryProviderDependencies<Head>, ...FundingProviderDependencies<Tail>]\n\t: readonly [];\n\ntype AccountDependencyMembers<Funding extends AccountFunding> = readonly [\n\ttypeof suiResource,\n\t...FundingCoinDependencies<Funding>,\n\t...FundingProviderDependencies<Funding>,\n];\n\nconst isCoinFundingEntry = (entry: AccountFundingEntry): entry is CrossCuttingFundingEntry =>\n\ttypeof entry === 'object' && entry.coin !== 'sui';\n\nconst fundingProviders = (\n\tprovider: CrossCuttingFundingProvider | undefined,\n): ReadonlyArray<AnyResourceRef> => {\n\tif (provider === undefined) return [];\n\treturn Array.isArray(provider)\n\t\t? (provider as ReadonlyArray<AnyResourceRef>)\n\t\t: [provider as AnyResourceRef];\n};\n\n/** Validate a SUI funding amount and project it to bigint MIST.\n * Returns a typed-error Effect so a bad amount surfaces as\n * `AccountAcquireError(phase: 'validate-funding')` on the failure\n * channel — NOT a synchronous throw lifted to a defect when called\n * from inside the acquire body's `Effect.gen`. */\nconst fundingAmountToBigInt = (\n\tamount: number | bigint,\n\taccountName: string,\n): Effect.Effect<bigint, AccountAcquireError> => {\n\tif (typeof amount === 'bigint') {\n\t\tif (amount < 0n) {\n\t\t\treturn Effect.fail(\n\t\t\t\taccountAcquireError({\n\t\t\t\t\tphase: 'validate-funding',\n\t\t\t\t\taccountName,\n\t\t\t\t\tvariant: 'ephemeral',\n\t\t\t\t\tmessage: `Account '${accountName}': SUI funding amount must be a non-negative integer in MIST (got ${amount}).`,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\treturn Effect.succeed(amount);\n\t}\n\tif (!Number.isSafeInteger(amount) || amount < 0) {\n\t\treturn Effect.fail(\n\t\t\taccountAcquireError({\n\t\t\t\tphase: 'validate-funding',\n\t\t\t\taccountName,\n\t\t\t\tvariant: 'ephemeral',\n\t\t\t\tmessage: `Account '${accountName}': SUI funding amount must be a non-negative safe integer in MIST (got ${amount}).`,\n\t\t\t}),\n\t\t);\n\t}\n\treturn Effect.succeed(BigInt(amount));\n};\n\nconst coinLabelFor = (coin: { readonly fullCoinType: string; readonly symbol?: string }): string =>\n\tcoin.symbol ?? coin.fullCoinType.split('::').at(-1) ?? coin.fullCoinType;\n\nconst inputAmount = (amount: number | bigint): string => amount.toString();\n\nconst inputFundingProviderIds = (\n\tprovider: CrossCuttingFundingProvider | undefined,\n): ReadonlyArray<string> =>\n\tfundingProviders(provider)\n\t\t.map((ref) => ref.id)\n\t\t.sort((a, b) => a.localeCompare(b));\n\nconst inputFundingIdentityFor = (entry: AccountFundingEntry): unknown => {\n\tif (entry.coin === 'sui') {\n\t\treturn { coin: 'sui', amountMist: inputAmount(entry.amount) };\n\t}\n\tconst via = inputFundingProviderIds(entry.via);\n\treturn {\n\t\tcoin: entry.coin.id,\n\t\tamountMist: entry.amount.toString(),\n\t\t...(via.length === 0 ? {} : { via }),\n\t};\n};\n\nconst accountFundingInputIdentity = (\n\topts: ResolvedAccountOptions,\n\tfundingEntries: AccountFunding,\n): ReadonlyArray<unknown> => {\n\tconst explicit = fundingEntries.map(inputFundingIdentityFor);\n\tif (opts.kind === 'ephemeral' && opts.funding === undefined) {\n\t\treturn [{ coin: 'sui', amountMist: DEFAULT_EPHEMERAL_FUND_MIST.toString() }, ...explicit];\n\t}\n\treturn explicit;\n};\n\nconst accountVariantInputIdentity = (opts: ResolvedAccountOptions): unknown => {\n\tswitch (opts.kind) {\n\t\tcase 'ephemeral':\n\t\t\treturn { kind: 'ephemeral' };\n\t\tcase 'impersonate':\n\t\t\treturn { kind: 'impersonate', address: opts.address };\n\t\tcase 'signer':\n\t\t\treturn {\n\t\t\t\tkind: 'signer',\n\t\t\t\taddress: opts.addressOverride ?? opts.signer.toSuiAddress(),\n\t\t\t};\n\t}\n};\n\nconst accountInputIdentity = (\n\topts: ResolvedAccountOptions,\n\tfundingEntries: AccountFunding,\n): unknown => ({\n\tplugin: 'account',\n\tname: opts.name,\n\tvariant: accountVariantInputIdentity(opts),\n\tfunding: accountFundingInputIdentity(opts, fundingEntries),\n});\n\n// ---------------------------------------------------------------------------\n// Capability emission — dynamic (POST-acquire). Receives the resolved\n// `AccountValue` + the identity so snapshot + codegen + registry + projection\n// decls reference the REAL address (and identity app/stack) — the static form\n// would force placeholder values for fields only known at acquire time.\n//\n// The account `start` emits its four contributions inline — snapshot →\n// codegen → registry → projection — directly via the typed `ctx` verbs,\n// AFTER resolving the value. The decl shapes + emit ORDER are load-bearing.\n// ---------------------------------------------------------------------------\n\nexport const account = <const N extends string, const Funding extends AccountFunding = readonly []>(\n\tname: N,\n\topts?: AccountOptions<Funding>,\n) => {\n\tassertAccountName(name);\n\n\t// Normalize bare-form to ephemeral default. The user-facing\n\t// `AccountOptions` union does not include \"kind absent\" — we\n\t// inject the default here so the rest of the body sees a\n\t// fully-discriminated value.\n\tconst opts2: ResolvedAccountOptions =\n\t\topts === undefined\n\t\t\t? { kind: 'ephemeral', name }\n\t\t\t: ({ ...opts, name } as ResolvedAccountOptions);\n\n\tconst accountRef = accountResource(name);\n\n\t// Pull the funding resource tuple out of opts (may be undefined for\n\t// the bare form / variants without funding). The tuple preserves\n\t// plugin-valued refs when the caller passed coin plugins, so\n\t// recursive stack composition can include those publishers.\n\tconst fundingEntries = (opts?.funding ?? []) as unknown as Funding;\n\tconst fundingEntryList = fundingEntries as AccountFunding;\n\tconst coinFundingEntries = fundingEntryList.filter(isCoinFundingEntry);\n\tconst fundingMembers = coinFundingEntries.map((e) => e.coin);\n\tconst strategyProviderMembers = coinFundingEntries.flatMap((entry) =>\n\t\tfundingProviders(entry.via),\n\t);\n\tconst dependencies = [\n\t\tsuiResource,\n\t\t...fundingMembers,\n\t\t...strategyProviderMembers,\n\t] as unknown as AccountDependencyMembers<Funding>;\n\n\treturn definePlugin({\n\t\tid: accountRef.id,\n\t\tdependsOn: dependencies,\n\t\t// Account is a value-producer (no long-lived server / container);\n\t\t// tasks acquire their value, publish contributions, then reach\n\t\t// `done`.\n\t\trole: 'task',\n\t\tsection: 'account',\n\t\tinputIdentity: staticInputIdentity(accountInputIdentity(opts2, fundingEntryList)),\n\t\t// `deps` auto-infers from the resolved `dependsOn`; `ctx` arrives\n\t\t// via the `PluginContext` service.\n\t\tstart: (deps) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst ctx = yield* PluginContext;\n\t\t\t\tconst [sui, ...resolvedDeps] = deps;\n\t\t\t\tconst resolvedCoinValues = resolvedDeps.slice(\n\t\t\t\t\t0,\n\t\t\t\t\tcoinFundingEntries.length,\n\t\t\t\t) as ReadonlyArray<{\n\t\t\t\t\treadonly fullCoinType: string;\n\t\t\t\t\treadonly symbol?: string;\n\t\t\t\t}>;\n\t\t\t\t// Identity + on-disk runtime root come from the\n\t\t\t\t// supervisor-provided substrate context.\n\t\t\t\tconst identity = yield* IdentityContext;\n\t\t\t\tconst paths = yield* StackPathsService;\n\n\t\t\t\t// Project each funding dependency value to a\n\t\t\t\t// `{fullCoinType, amount}` projection. Dependency order\n\t\t\t\t// mirrors `fundingEntries`, after the hard Sui upstream.\n\t\t\t\t// SUI amount validation lives on the failure channel so a\n\t\t\t\t// negative / non-integer amount surfaces as a typed\n\t\t\t\t// `AccountAcquireError(phase: 'validate-funding')` instead\n\t\t\t\t// of a defect (the old sync-throw lifted to a defect when\n\t\t\t\t// run inside this `Effect.gen`).\n\t\t\t\tlet coinIndex = 0;\n\t\t\t\tconst projectedFundingMutable: ProjectedFundingEntry[] = [];\n\t\t\t\tfor (const entry of fundingEntryList) {\n\t\t\t\t\tif (entry.coin === 'sui') {\n\t\t\t\t\t\tconst amount = yield* fundingAmountToBigInt(entry.amount, name);\n\t\t\t\t\t\tprojectedFundingMutable.push({\n\t\t\t\t\t\t\tcoin: 'SUI',\n\t\t\t\t\t\t\tfullCoinType: SUI_FULL_COIN_TYPE,\n\t\t\t\t\t\t\tamount,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tconst resolvedCoin = resolvedCoinValues[coinIndex]!;\n\t\t\t\t\tcoinIndex += 1;\n\t\t\t\t\tprojectedFundingMutable.push({\n\t\t\t\t\t\tcoin: coinLabelFor(resolvedCoin),\n\t\t\t\t\t\tfullCoinType: resolvedCoin.fullCoinType,\n\t\t\t\t\t\tamount: entry.amount,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tconst projectedFunding: ReadonlyArray<ProjectedFundingEntry> = projectedFundingMutable;\n\n\t\t\t\tconst acquireCtx: AccountAcquireContext = {\n\t\t\t\t\tsui: {\n\t\t\t\t\t\tmode: sui.mode,\n\t\t\t\t\t\tchainId: sui.chainId,\n\t\t\t\t\t\tsdk: sui.sdk,\n\t\t\t\t\t\tfork: sui.fork,\n\t\t\t\t\t},\n\t\t\t\t\truntimeRoot: paths.stackRoot,\n\t\t\t\t\tapp: identity.app,\n\t\t\t\t\tstack: identity.stack,\n\t\t\t\t\temitAutoPromotionEvent: () =>\n\t\t\t\t\t\tEffect.logWarning('account funding auto-promoted for fork mode').pipe(\n\t\t\t\t\t\t\tEffect.annotateLogs({\n\t\t\t\t\t\t\t\t'account.name': name,\n\t\t\t\t\t\t\t\t'account.funding.from': 'faucet',\n\t\t\t\t\t\t\t\t'account.funding.to': 'pay-from-seed-via-impersonate',\n\t\t\t\t\t\t\t\t[SuiLogAttr.mode]: 'fork',\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t),\n\t\t\t\t\tprojectedFunding,\n\t\t\t\t};\n\t\t\t\tconst resolved = yield* acquireAccount(opts2, acquireCtx);\n\n\t\t\t\t// Emit the resolved account's contributions inline, in order:\n\t\t\t\t// snapshot → codegen → registry → projection. `resolved` is the\n\t\t\t\t// just-acquired `AccountValue`; `identity` (from\n\t\t\t\t// `IdentityContext`) supplies the snapshot's pre-restore guard\n\t\t\t\t// app/stack; the variant kind is `opts2.kind`. The registry +\n\t\t\t\t// projection decls share one `realEntry` so the registered\n\t\t\t\t// strategy and the published event agree byte-for-byte.\n\t\t\t\tconst realEntry: AccountRegistryEntry = {\n\t\t\t\t\tname,\n\t\t\t\t\taddress: resolved.address,\n\t\t\t\t\tscheme: resolved.scheme,\n\t\t\t\t\tsource: resolved.source,\n\t\t\t\t\tfunding: fundingProjectionForResult(resolved.funding),\n\t\t\t\t};\n\t\t\t\tctx.snapshotExtra(\n\t\t\t\t\tmakeAccountSnapshotable({\n\t\t\t\t\t\taccountName: name,\n\t\t\t\t\t\tvariant: opts2.kind,\n\t\t\t\t\t\tapp: identity.app,\n\t\t\t\t\t\tstack: identity.stack,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\tctx.codegen(\n\t\t\t\t\tmakeAccountCodegen<N>({\n\t\t\t\t\t\tname,\n\t\t\t\t\t\tresolved: {\n\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\taddress: resolved.address,\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\tctx.provides(\n\t\t\t\t\tmakeAccountRegistryContribution<N>(\n\t\t\t\t\t\trealEntry as AccountRegistryEntry & { readonly name: N },\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\tctx.publish(\n\t\t\t\t\tmakeAccountProjectionContribution<N>(\n\t\t\t\t\t\trealEntry as AccountRegistryEntry & { readonly name: N },\n\t\t\t\t\t),\n\t\t\t\t);\n\n\t\t\t\treturn resolved;\n\t\t\t}),\n\t});\n};\n\n/** @internal — exported only for the projection regression test\n * (`test/plugins/account/funding-projection.test.ts`). Projects the runtime\n * `AccountFundingResult` (requested + applied) into the registry's\n * per-row funded/already-satisfied/skipped status. */\nexport const fundingProjectionForResult = (\n\tfunding: AccountFundingResult,\n): AccountRegistryFunding => {\n\tif (funding.requested.length === 0) {\n\t\treturn { status: 'skipped', balanceMist: null, requestedMist: null, entries: [] };\n\t}\n\n\t// Positional outcome consume so the projection can distinguish a\n\t// real faucet call (`funded`) from the pre-existing-balance\n\t// short-circuit (`already-satisfied`). `applied` is an\n\t// order-preserving SUBSEQUENCE of `requested`:\n\t// `applyCrossCuttingFunding` (and the default-funding pass) iterate\n\t// `requested` in order and only push a processed entry onto\n\t// `applied`, dropping zero-amount and no-strategy entries. So we\n\t// walk a cursor through `applied` and adopt its outcome for the\n\t// matching requested row by (fullCoinType, amount) — a keyed Map\n\t// would collapse duplicate (coin, amount) entries and mislabel the\n\t// genuinely-funded first row as `already-satisfied`. An entry the\n\t// funding pass never reached (zero-amount, or absent from `applied`)\n\t// does not match the cursor and projects as `skipped` without\n\t// advancing it.\n\tlet appliedCursor = 0;\n\tconst entries = funding.requested.map((entry) => {\n\t\tconst candidate = funding.applied[appliedCursor];\n\t\tconst matches =\n\t\t\tcandidate !== undefined &&\n\t\t\tcandidate.fullCoinType === entry.fullCoinType &&\n\t\t\tcandidate.amount === entry.amount;\n\t\tconst outcome = matches ? candidate.outcome : undefined;\n\t\tif (matches) {\n\t\t\tappliedCursor += 1;\n\t\t}\n\t\treturn {\n\t\t\tcoin: entry.coin,\n\t\t\tfullCoinType: entry.fullCoinType,\n\t\t\tamount: entry.amount.toString(),\n\t\t\tstatus:\n\t\t\t\toutcome === 'funded'\n\t\t\t\t\t? ('funded' as const)\n\t\t\t\t\t: outcome === 'already-satisfied'\n\t\t\t\t\t\t? ('already-satisfied' as const)\n\t\t\t\t\t\t: ('skipped' as const),\n\t\t};\n\t});\n\tconst requestedMist =\n\t\tfunding.requested\n\t\t\t.find((entry) => entry.fullCoinType === SUI_FULL_COIN_TYPE)\n\t\t\t?.amount.toString() ?? null;\n\tconst settledCount = entries.filter(\n\t\t(entry) => entry.status === 'funded' || entry.status === 'already-satisfied',\n\t).length;\n\treturn {\n\t\t// `funded` here means \"every requested entry is satisfied\" —\n\t\t// either the faucet succeeded or the pre-existing balance\n\t\t// already covered it. `unknown` covers the mixed partial-success\n\t\t// case so consumers can distinguish from \"nothing ran at all\".\n\t\tstatus: settledCount === entries.length ? 'funded' : settledCount === 0 ? 'skipped' : 'unknown',\n\t\tbalanceMist: null,\n\t\trequestedMist,\n\t\tentries,\n\t};\n};\n\n// ---------------------------------------------------------------------------\n// Re-exports for advanced callers (Coin, Wallet, Package)\n// ---------------------------------------------------------------------------\n\nexport type {\n\tAccountOptions,\n\tAccountSignerInput,\n\tResolvedAccountOptions,\n\tAccountValue,\n\tSignAndExecuteResult,\n\tTxResult,\n} from './service.ts';\nexport type {\n\tAccountError,\n\tAccountAcquireError,\n\tAccountAcquirePhase,\n\tAccountSignError,\n\tAccountSignPhase,\n\tAccountVariantKind,\n} from './errors.ts';\nexport type {\n\tAccountFunding,\n\tAccountFundingEntry,\n\tAccountFundingCoinValue,\n\tAccountFundingResult,\n\tAccountFundingRequest,\n\tAccountFundingStrategy,\n\tAppliedFunding,\n\tAppliedFundingEntry,\n\tAppliedFundingOutcome,\n\tCoinMember,\n\tCrossCuttingFundingEntry,\n\tCrossCuttingFundingProvider,\n\tFundEphemeralDefaultOutcome,\n\tProjectedFunding,\n\tProjectedFundingEntry,\n\tSuiFundingEntry,\n} from './funding.ts';\nexport { DEFAULT_EPHEMERAL_FUND_MIST, SUI_FULL_COIN_TYPE } from './funding.ts';\nexport type { AccountBindings } from './codegen.ts';\nexport type {\n\tAccountRegistryEntry,\n\tAccountRegistryFunding,\n\tAccountRegistryKey,\n} from './registry.ts';\nexport { accountRegistryKey } from './registry.ts';\nexport type { SyntheticImpersonationSigner } from './variants/impersonate.ts';\nexport type { SignatureScheme, ResolvedKeypair } from './keypair.ts';\n"],"mappings":"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"}
@@ -2,14 +2,14 @@ import { keyedBucketSpec, liveBucketCodegen, staticBucketCodegen } from "../../c
2
2
  //#region src/plugins/coin/codegen.ts
3
3
  /** Build the coin's config-binding spec for symbol `key`. `symbol` and
4
4
  * `source` are structural literals; the coin type, decimals, and on-chain
5
- * object ids are runtime-resolved (`resolveValue`).
5
+ * object ids are runtime-resolved (`requireValue(dep, …)`).
6
6
  *
7
- * Field-set DETERMINISM. A committed `resolveValue(...)` call evaluates at
7
+ * Field-set DETERMINISM. A committed `requireValue(dep, …)` call evaluates at
8
8
  * module-load and THROWS when the id is absent, so the static stub must
9
9
  * emit ONLY fields the injected ids will carry. `source` (known at factory
10
10
  * time) decides this: a `'builtin'` coin (SUI) is fully protocol-defined —
11
11
  * it carries NO package id and its coin type / decimals are constants, so
12
- * it emits them as LITERALS (no `resolveValue` that would throw at load).
12
+ * it emits them as LITERALS (no `requireValue(dep, …)` that would throw at load).
13
13
  * A `'registry'` / `'on-chain'` coin carries a real `packageId`, so it
14
14
  * emits `fullCoinType` / `decimals` / `packageId` as resolved. The OPTIONAL
15
15
  * discovery-only ids (`treasuryCapId` / `metadataId` / `iconUrl` /
@@ -87,14 +87,14 @@ const coinBucketSpec = (key, structural, live) => {
87
87
  });
88
88
  };
89
89
  /** Construct the LIVE Codegenable contribution for one coin instance.
90
- * Bakes the resolved record + feeds the generic id-config `values` channel.
90
+ * Bakes the resolved record + feeds the generic deployment `values` channel.
91
91
  * Mirrors `account/${name}` naming. */
92
92
  const makeCoinCodegen = (parts) => liveBucketCodegen(coinBucketSpec(parts.symbol, {
93
93
  symbol: parts.resolved.symbol,
94
94
  source: parts.resolved.source
95
95
  }, parts.resolved), parts.resolved);
96
96
  /** Construct the STATIC (stack-free) Codegenable contribution for one coin.
97
- * Emits `resolveValue('coin:<symbol>', '<key>')` for runtime fields; the
97
+ * Emits `requireValue(dep, 'coin:<symbol>', '<key>')` for runtime fields; the
98
98
  * committed `coins.ts` carries no baked coin type / object id. */
99
99
  const makeCoinStaticCodegen = (config) => staticBucketCodegen(coinBucketSpec(config.symbol, config, null));
100
100
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"codegen.mjs","names":[],"sources":["../../../src/plugins/coin/codegen.ts"],"sourcesContent":["// Coin plugin — Codegenable contribution, via the UNIFIED config-binding\n// declaration.\n//\n// Distilled-doc 13-coin.md §\"Files written\": the codegen orchestrator\n// projects the per-stack CoinRegistry into a generated `coins.ts` table:\n//\n// export const coins = {\n// mUSDC: { type, decimals, sdkCoin, symbol, ... },\n// mWETH: { type, decimals, sdkCoin, symbol, ... },\n// } as const;\n//\n// ONE declaration, TWO derivations. A coin declares its `coins.ts`\n// contribution ONCE as a `ConfigBindingSet` (rooted under its symbol key).\n// The framework derives both behaviors (see `contracts/config-bindings.ts`):\n// - LIVE (boot): bakes the resolved on-chain values (fullCoinType,\n// decimals, ids) AND feeds the generic id-config `values` channel.\n// - STATIC (committed tree): emits `resolveValue('coin:<symbol>', '<key>')`\n// so the committed `coins.ts` carries NO baked coin type / object id.\n//\n// STRUCTURAL fields (symbol, source) stay literals; the coin type, decimals,\n// icon URL, and on-chain object ids are RUNTIME (loaded config data),\n// resolved at app build/dev time via the injected `__DEVSTACK_IDS__` global.\n\nimport type { CodegenableDecl } from '../../contracts/codegenable.ts';\nimport {\n\tkeyedBucketSpec,\n\tliveBucketCodegen,\n\tstaticBucketCodegen,\n\ttype BucketField,\n\ttype SiblingBucketSpec,\n} from '../../contracts/config-bindings.ts';\nimport type { JsonValue } from '../../orchestrators/codegen/id-config.ts';\n\n/** The typed shape per emitted coin record. */\nexport interface CoinBindings {\n\treadonly symbol: string;\n\treadonly fullCoinType: string;\n\treadonly decimals: number;\n\treadonly displayName?: string;\n\treadonly iconUrl?: string;\n\treadonly treasuryCapId?: string;\n\treadonly metadataId?: string;\n\treadonly packageId?: string;\n\treadonly source: 'registry' | 'on-chain' | 'builtin';\n}\n\n/** Static-config shape a coin can describe BEFORE acquire — the fields the\n * stack-free `staticCodegen` hook needs to emit a deterministic committed\n * stub for this symbol. `source` is the address-form provenance (known at\n * factory time). `constants` is set ONLY for a `'builtin'` coin (SUI),\n * whose coin type + decimals are protocol-defined constants the stub bakes\n * as literals (no `resolveValue` that would throw at module load). */\nexport interface CoinStaticConfig {\n\treadonly symbol: string;\n\treadonly source: CoinBindings['source'];\n\treadonly constants?: { readonly fullCoinType: string; readonly decimals: number };\n\t/** A user-declared on-chain coin type (the `coin.known('0x…::m::W')`\n\t * argument). This is DECLARED config (not loaded-at-runtime data), so the\n\t * committed `coins.ts` bakes it as a LITERAL `fullCoinType`. `decimals` and\n\t * `packageId` still resolve at runtime (they come from `getCoinMetadata`,\n\t * genuinely only known after a live probe), so they stay `resolveValue`.\n\t * Absent for `fromPackage` (registry/local) coins whose type is dynamic. */\n\treadonly knownCoinType?: string;\n}\n\n/** The state the LIVE binding derivation reads — the resolved coin record. */\ntype CoinLiveState = CoinBindings;\n\n/** Build the coin's config-binding spec for symbol `key`. `symbol` and\n * `source` are structural literals; the coin type, decimals, and on-chain\n * object ids are runtime-resolved (`resolveValue`).\n *\n * Field-set DETERMINISM. A committed `resolveValue(...)` call evaluates at\n * module-load and THROWS when the id is absent, so the static stub must\n * emit ONLY fields the injected ids will carry. `source` (known at factory\n * time) decides this: a `'builtin'` coin (SUI) is fully protocol-defined —\n * it carries NO package id and its coin type / decimals are constants, so\n * it emits them as LITERALS (no `resolveValue` that would throw at load).\n * A `'registry'` / `'on-chain'` coin carries a real `packageId`, so it\n * emits `fullCoinType` / `decimals` / `packageId` as resolved. The OPTIONAL\n * discovery-only ids (`treasuryCapId` / `metadataId` / `iconUrl` /\n * `displayName`) are non-deterministic — the static stub omits them; the\n * LIVE record emits whatever it actually surfaced (consumers read them\n * through optional chaining). */\nconst coinBucketSpec = (\n\tkey: string,\n\tstructural: CoinStaticConfig,\n\tlive: CoinLiveState | null,\n): SiblingBucketSpec<CoinLiveState> => {\n\tconst builtin = structural.source === 'builtin';\n\tconst fields: Array<BucketField<CoinLiveState>> = [\n\t\t{ key: 'symbol', variant: 'literal', value: structural.symbol },\n\t\t{ key: 'source', variant: 'literal', value: structural.source },\n\t];\n\tconst constants =\n\t\tstructural.constants ??\n\t\t(live !== null && builtin\n\t\t\t? { fullCoinType: live.fullCoinType, decimals: live.decimals }\n\t\t\t: undefined);\n\tif (builtin && constants !== undefined) {\n\t\t// A builtin coin (SUI) is protocol-defined: bake its constants as\n\t\t// literals in BOTH paths (no `resolveValue` to throw at module load).\n\t\t// The coin type carries `::` so it is not a baked on-chain id.\n\t\tfields.push({ key: 'fullCoinType', variant: 'literal', value: constants.fullCoinType });\n\t\tfields.push({ key: 'decimals', variant: 'literal', value: constants.decimals });\n\t} else {\n\t\t// A package / on-chain coin: decimals + package id are LOADED CONFIG\n\t\t// DATA (from `getCoinMetadata`) — resolve at app build/dev time. The\n\t\t// `fullCoinType` is DECLARED config when the caller passed an explicit\n\t\t// `coin.known('0x…::m::W')` type (`knownCoinType`) — bake it as a\n\t\t// LITERAL; otherwise (`fromPackage`) it is dynamic, so resolve it.\n\t\tif (structural.knownCoinType !== undefined) {\n\t\t\tfields.push({ key: 'fullCoinType', variant: 'literal', value: structural.knownCoinType });\n\t\t} else {\n\t\t\tfields.push({\n\t\t\t\tkey: 'fullCoinType',\n\t\t\t\tvariant: 'resolved',\n\t\t\t\ttsType: 'string',\n\t\t\t\tlive: (s) => s.fullCoinType,\n\t\t\t});\n\t\t}\n\t\tfields.push({\n\t\t\tkey: 'decimals',\n\t\t\tvariant: 'resolved',\n\t\t\ttsType: 'number',\n\t\t\tlive: (s) => s.decimals,\n\t\t});\n\t\tfields.push({\n\t\t\tkey: 'packageId',\n\t\t\tvariant: 'resolved',\n\t\t\ttsType: 'string | null',\n\t\t\tlive: (s) => (s.packageId ?? null) as JsonValue,\n\t\t});\n\t}\n\t// Discovery-only optional ids — emitted ONLY on the LIVE path, and only\n\t// when the resolved record carries them (non-deterministic, so the\n\t// committed static stub omits them; consumers read via optional chaining).\n\tif (live !== null) {\n\t\tconst optional: ReadonlyArray<keyof CoinBindings> = [\n\t\t\t'displayName',\n\t\t\t'iconUrl',\n\t\t\t'treasuryCapId',\n\t\t\t'metadataId',\n\t\t];\n\t\tfor (const field of optional) {\n\t\t\tif (live[field] !== undefined) {\n\t\t\t\tfields.push({\n\t\t\t\t\tkey: field,\n\t\t\t\t\tvariant: 'resolved',\n\t\t\t\t\tlive: (s) => (s[field] ?? null) as JsonValue,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\treturn keyedBucketSpec({ bucket: 'coins.ts', kind: 'coin', key, fields });\n};\n\n/** Construct the LIVE Codegenable contribution for one coin instance.\n * Bakes the resolved record + feeds the generic id-config `values` channel.\n * Mirrors `account/${name}` naming. */\nexport const makeCoinCodegen = <Symbol extends string>(parts: {\n\treadonly symbol: Symbol;\n\treadonly resolved: CoinBindings;\n}): CodegenableDecl =>\n\tliveBucketCodegen(\n\t\tcoinBucketSpec(\n\t\t\tparts.symbol,\n\t\t\t{ symbol: parts.resolved.symbol, source: parts.resolved.source },\n\t\t\tparts.resolved,\n\t\t),\n\t\tparts.resolved,\n\t);\n\n/** Construct the STATIC (stack-free) Codegenable contribution for one coin.\n * Emits `resolveValue('coin:<symbol>', '<key>')` for runtime fields; the\n * committed `coins.ts` carries no baked coin type / object id. */\nexport const makeCoinStaticCodegen = (config: CoinStaticConfig): CodegenableDecl =>\n\tstaticBucketCodegen(coinBucketSpec(config.symbol, config, null));\n"],"mappings":";;;;;;;;;;;;;;;;;;AAoFA,MAAM,kBACL,KACA,YACA,SACsC;CACtC,MAAM,UAAU,WAAW,WAAW;CACtC,MAAM,SAA4C,CACjD;EAAE,KAAK;EAAU,SAAS;EAAW,OAAO,WAAW;CAAO,GAC9D;EAAE,KAAK;EAAU,SAAS;EAAW,OAAO,WAAW;CAAO,CAC/D;CACA,MAAM,YACL,WAAW,cACV,SAAS,QAAQ,UACf;EAAE,cAAc,KAAK;EAAc,UAAU,KAAK;CAAS,IAC3D,KAAA;CACJ,IAAI,WAAW,cAAc,KAAA,GAAW;EAIvC,OAAO,KAAK;GAAE,KAAK;GAAgB,SAAS;GAAW,OAAO,UAAU;EAAa,CAAC;EACtF,OAAO,KAAK;GAAE,KAAK;GAAY,SAAS;GAAW,OAAO,UAAU;EAAS,CAAC;CAC/E,OAAO;EAMN,IAAI,WAAW,kBAAkB,KAAA,GAChC,OAAO,KAAK;GAAE,KAAK;GAAgB,SAAS;GAAW,OAAO,WAAW;EAAc,CAAC;OAExF,OAAO,KAAK;GACX,KAAK;GACL,SAAS;GACT,QAAQ;GACR,OAAO,MAAM,EAAE;EAChB,CAAC;EAEF,OAAO,KAAK;GACX,KAAK;GACL,SAAS;GACT,QAAQ;GACR,OAAO,MAAM,EAAE;EAChB,CAAC;EACD,OAAO,KAAK;GACX,KAAK;GACL,SAAS;GACT,QAAQ;GACR,OAAO,MAAO,EAAE,aAAa;EAC9B,CAAC;CACF;CAIA,IAAI,SAAS;OAOP,MAAM,SAAS;GALnB;GACA;GACA;GACA;EAE0B,GAC1B,IAAI,KAAK,WAAW,KAAA,GACnB,OAAO,KAAK;GACX,KAAK;GACL,SAAS;GACT,OAAO,MAAO,EAAE,UAAU;EAC3B,CAAC;CAAA;CAIJ,OAAO,gBAAgB;EAAE,QAAQ;EAAY,MAAM;EAAQ;EAAK;CAAO,CAAC;AACzE;;;;AAKA,MAAa,mBAA0C,UAItD,kBACC,eACC,MAAM,QACN;CAAE,QAAQ,MAAM,SAAS;CAAQ,QAAQ,MAAM,SAAS;AAAO,GAC/D,MAAM,QACP,GACA,MAAM,QACP;;;;AAKD,MAAa,yBAAyB,WACrC,oBAAoB,eAAe,OAAO,QAAQ,QAAQ,IAAI,CAAC"}
1
+ {"version":3,"file":"codegen.mjs","names":[],"sources":["../../../src/plugins/coin/codegen.ts"],"sourcesContent":["// Coin plugin — Codegenable contribution, via the UNIFIED config-binding\n// declaration.\n//\n// Distilled-doc 13-coin.md §\"Files written\": the codegen orchestrator\n// projects the per-stack CoinRegistry into a generated `coins.ts` table:\n//\n// export const coins = {\n// mUSDC: { type, decimals, sdkCoin, symbol, ... },\n// mWETH: { type, decimals, sdkCoin, symbol, ... },\n// } as const;\n//\n// ONE declaration, TWO derivations. A coin declares its `coins.ts`\n// contribution ONCE as a `ConfigBindingSet` (rooted under its symbol key).\n// The framework derives both behaviors (see `contracts/config-bindings.ts`):\n// - LIVE (boot): bakes the resolved on-chain values (fullCoinType,\n// decimals, ids) AND feeds the generic deployment `values` channel.\n// - STATIC (committed tree): emits `requireValue(dep, 'coin:<symbol>', '<key>')`\n// so the committed `coins.ts` carries NO baked coin type / object id.\n//\n// STRUCTURAL fields (symbol, source) stay literals; the coin type, decimals,\n// icon URL, and on-chain object ids are RUNTIME (loaded config data),\n// resolved at app build/dev time via the injected `__DEVSTACK_DEPLOYMENT__` global.\n\nimport type { CodegenableDecl } from '../../contracts/codegenable.ts';\nimport {\n\tkeyedBucketSpec,\n\tliveBucketCodegen,\n\tstaticBucketCodegen,\n\ttype BucketField,\n\ttype SiblingBucketSpec,\n} from '../../contracts/config-bindings.ts';\nimport type { JsonValue } from '../../orchestrators/codegen/deployment.ts';\n\n/** The typed shape per emitted coin record. */\nexport interface CoinBindings {\n\treadonly symbol: string;\n\treadonly fullCoinType: string;\n\treadonly decimals: number;\n\treadonly displayName?: string;\n\treadonly iconUrl?: string;\n\treadonly treasuryCapId?: string;\n\treadonly metadataId?: string;\n\treadonly packageId?: string;\n\treadonly source: 'registry' | 'on-chain' | 'builtin';\n}\n\n/** Static-config shape a coin can describe BEFORE acquire — the fields the\n * stack-free `staticCodegen` hook needs to emit a deterministic committed\n * stub for this symbol. `source` is the address-form provenance (known at\n * factory time). `constants` is set ONLY for a `'builtin'` coin (SUI),\n * whose coin type + decimals are protocol-defined constants the stub bakes\n * as literals (no `requireValue(dep, …)` that would throw at module load). */\nexport interface CoinStaticConfig {\n\treadonly symbol: string;\n\treadonly source: CoinBindings['source'];\n\treadonly constants?: { readonly fullCoinType: string; readonly decimals: number };\n\t/** A user-declared on-chain coin type (the `coin.known('0x…::m::W')`\n\t * argument). This is DECLARED config (not loaded-at-runtime data), so the\n\t * committed `coins.ts` bakes it as a LITERAL `fullCoinType`. `decimals` and\n\t * `packageId` still resolve at runtime (they come from `getCoinMetadata`,\n\t * genuinely only known after a live probe), so they stay `requireValue(dep, …)`.\n\t * Absent for `fromPackage` (registry/local) coins whose type is dynamic. */\n\treadonly knownCoinType?: string;\n}\n\n/** The state the LIVE binding derivation reads — the resolved coin record. */\ntype CoinLiveState = CoinBindings;\n\n/** Build the coin's config-binding spec for symbol `key`. `symbol` and\n * `source` are structural literals; the coin type, decimals, and on-chain\n * object ids are runtime-resolved (`requireValue(dep, …)`).\n *\n * Field-set DETERMINISM. A committed `requireValue(dep, …)` call evaluates at\n * module-load and THROWS when the id is absent, so the static stub must\n * emit ONLY fields the injected ids will carry. `source` (known at factory\n * time) decides this: a `'builtin'` coin (SUI) is fully protocol-defined —\n * it carries NO package id and its coin type / decimals are constants, so\n * it emits them as LITERALS (no `requireValue(dep, …)` that would throw at load).\n * A `'registry'` / `'on-chain'` coin carries a real `packageId`, so it\n * emits `fullCoinType` / `decimals` / `packageId` as resolved. The OPTIONAL\n * discovery-only ids (`treasuryCapId` / `metadataId` / `iconUrl` /\n * `displayName`) are non-deterministic — the static stub omits them; the\n * LIVE record emits whatever it actually surfaced (consumers read them\n * through optional chaining). */\nconst coinBucketSpec = (\n\tkey: string,\n\tstructural: CoinStaticConfig,\n\tlive: CoinLiveState | null,\n): SiblingBucketSpec<CoinLiveState> => {\n\tconst builtin = structural.source === 'builtin';\n\tconst fields: Array<BucketField<CoinLiveState>> = [\n\t\t{ key: 'symbol', variant: 'literal', value: structural.symbol },\n\t\t{ key: 'source', variant: 'literal', value: structural.source },\n\t];\n\tconst constants =\n\t\tstructural.constants ??\n\t\t(live !== null && builtin\n\t\t\t? { fullCoinType: live.fullCoinType, decimals: live.decimals }\n\t\t\t: undefined);\n\tif (builtin && constants !== undefined) {\n\t\t// A builtin coin (SUI) is protocol-defined: bake its constants as\n\t\t// literals in BOTH paths (no `requireValue(dep, …)` to throw at module load).\n\t\t// The coin type carries `::` so it is not a baked on-chain id.\n\t\tfields.push({ key: 'fullCoinType', variant: 'literal', value: constants.fullCoinType });\n\t\tfields.push({ key: 'decimals', variant: 'literal', value: constants.decimals });\n\t} else {\n\t\t// A package / on-chain coin: decimals + package id are LOADED CONFIG\n\t\t// DATA (from `getCoinMetadata`) — resolve at app build/dev time. The\n\t\t// `fullCoinType` is DECLARED config when the caller passed an explicit\n\t\t// `coin.known('0x…::m::W')` type (`knownCoinType`) — bake it as a\n\t\t// LITERAL; otherwise (`fromPackage`) it is dynamic, so resolve it.\n\t\tif (structural.knownCoinType !== undefined) {\n\t\t\tfields.push({ key: 'fullCoinType', variant: 'literal', value: structural.knownCoinType });\n\t\t} else {\n\t\t\tfields.push({\n\t\t\t\tkey: 'fullCoinType',\n\t\t\t\tvariant: 'resolved',\n\t\t\t\ttsType: 'string',\n\t\t\t\tlive: (s) => s.fullCoinType,\n\t\t\t});\n\t\t}\n\t\tfields.push({\n\t\t\tkey: 'decimals',\n\t\t\tvariant: 'resolved',\n\t\t\ttsType: 'number',\n\t\t\tlive: (s) => s.decimals,\n\t\t});\n\t\tfields.push({\n\t\t\tkey: 'packageId',\n\t\t\tvariant: 'resolved',\n\t\t\ttsType: 'string | null',\n\t\t\tlive: (s) => (s.packageId ?? null) as JsonValue,\n\t\t});\n\t}\n\t// Discovery-only optional ids — emitted ONLY on the LIVE path, and only\n\t// when the resolved record carries them (non-deterministic, so the\n\t// committed static stub omits them; consumers read via optional chaining).\n\tif (live !== null) {\n\t\tconst optional: ReadonlyArray<keyof CoinBindings> = [\n\t\t\t'displayName',\n\t\t\t'iconUrl',\n\t\t\t'treasuryCapId',\n\t\t\t'metadataId',\n\t\t];\n\t\tfor (const field of optional) {\n\t\t\tif (live[field] !== undefined) {\n\t\t\t\tfields.push({\n\t\t\t\t\tkey: field,\n\t\t\t\t\tvariant: 'resolved',\n\t\t\t\t\tlive: (s) => (s[field] ?? null) as JsonValue,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\treturn keyedBucketSpec({ bucket: 'coins.ts', kind: 'coin', key, fields });\n};\n\n/** Construct the LIVE Codegenable contribution for one coin instance.\n * Bakes the resolved record + feeds the generic deployment `values` channel.\n * Mirrors `account/${name}` naming. */\nexport const makeCoinCodegen = <Symbol extends string>(parts: {\n\treadonly symbol: Symbol;\n\treadonly resolved: CoinBindings;\n}): CodegenableDecl =>\n\tliveBucketCodegen(\n\t\tcoinBucketSpec(\n\t\t\tparts.symbol,\n\t\t\t{ symbol: parts.resolved.symbol, source: parts.resolved.source },\n\t\t\tparts.resolved,\n\t\t),\n\t\tparts.resolved,\n\t);\n\n/** Construct the STATIC (stack-free) Codegenable contribution for one coin.\n * Emits `requireValue(dep, 'coin:<symbol>', '<key>')` for runtime fields; the\n * committed `coins.ts` carries no baked coin type / object id. */\nexport const makeCoinStaticCodegen = (config: CoinStaticConfig): CodegenableDecl =>\n\tstaticBucketCodegen(coinBucketSpec(config.symbol, config, null));\n"],"mappings":";;;;;;;;;;;;;;;;;;AAoFA,MAAM,kBACL,KACA,YACA,SACsC;CACtC,MAAM,UAAU,WAAW,WAAW;CACtC,MAAM,SAA4C,CACjD;EAAE,KAAK;EAAU,SAAS;EAAW,OAAO,WAAW;CAAO,GAC9D;EAAE,KAAK;EAAU,SAAS;EAAW,OAAO,WAAW;CAAO,CAC/D;CACA,MAAM,YACL,WAAW,cACV,SAAS,QAAQ,UACf;EAAE,cAAc,KAAK;EAAc,UAAU,KAAK;CAAS,IAC3D,KAAA;CACJ,IAAI,WAAW,cAAc,KAAA,GAAW;EAIvC,OAAO,KAAK;GAAE,KAAK;GAAgB,SAAS;GAAW,OAAO,UAAU;EAAa,CAAC;EACtF,OAAO,KAAK;GAAE,KAAK;GAAY,SAAS;GAAW,OAAO,UAAU;EAAS,CAAC;CAC/E,OAAO;EAMN,IAAI,WAAW,kBAAkB,KAAA,GAChC,OAAO,KAAK;GAAE,KAAK;GAAgB,SAAS;GAAW,OAAO,WAAW;EAAc,CAAC;OAExF,OAAO,KAAK;GACX,KAAK;GACL,SAAS;GACT,QAAQ;GACR,OAAO,MAAM,EAAE;EAChB,CAAC;EAEF,OAAO,KAAK;GACX,KAAK;GACL,SAAS;GACT,QAAQ;GACR,OAAO,MAAM,EAAE;EAChB,CAAC;EACD,OAAO,KAAK;GACX,KAAK;GACL,SAAS;GACT,QAAQ;GACR,OAAO,MAAO,EAAE,aAAa;EAC9B,CAAC;CACF;CAIA,IAAI,SAAS;OAOP,MAAM,SAAS;GALnB;GACA;GACA;GACA;EAE0B,GAC1B,IAAI,KAAK,WAAW,KAAA,GACnB,OAAO,KAAK;GACX,KAAK;GACL,SAAS;GACT,OAAO,MAAO,EAAE,UAAU;EAC3B,CAAC;CAAA;CAIJ,OAAO,gBAAgB;EAAE,QAAQ;EAAY,MAAM;EAAQ;EAAK;CAAO,CAAC;AACzE;;;;AAKA,MAAa,mBAA0C,UAItD,kBACC,eACC,MAAM,QACN;CAAE,QAAQ,MAAM,SAAS;CAAQ,QAAQ,MAAM,SAAS;AAAO,GAC/D,MAAM,QACP,GACA,MAAM,QACP;;;;AAKD,MAAa,yBAAyB,WACrC,oBAAoB,eAAe,OAAO,QAAQ,QAAQ,IAAI,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/plugins/coin/index.ts"],"sourcesContent":["// Coin plugin — barrel + `coin(...)` factory family.\n//\n// Architecture (13-coin.md): Coin is the user-facing primitive family\n// for *addressing* custom Move coin types. It does NOT publish Move\n// modules itself — the Package plugin publishes; coin auto-discovery\n// (in `discovery.ts`) folds the publish output into the per-stack\n// `CoinRegistry`; this factory resolves user-supplied addresses\n// (witness / bare-type / builtin) against that registry plus the live RPC.\n//\n// User-facing factory shape — three variants mirroring the address\n// forms:\n//\n// coin.fromPackage(pkg, 'MOCK_USDC') // package member → registry\n// coin.known('0x...::deep::DEEP') // bare → live RPC\n// coin.builtin('sui') // protocol-defined constant\n//\n// (A `coin(identifier)` convenience entry is intentionally NOT\n// exposed — distilled-doc 13-coin.md Pain point #6 documents how the\n// \"guess the form from the string\" path is a footgun. The three-form\n// surface forces the user to make the disambiguation explicit at the\n// call site.)\n//\n// Resource id: `'coin:<package>/<witness>'` for package-scoped coins and\n// `'coin:<identifier>'` for known/builtin coins — one tag per declared\n// coin instance, so the substrate's compose-time dedup detects collisions\n// cleanly. Mirrors the Package plugin's per-instance resource identity.\n\nimport { createHash } from 'node:crypto';\n\nimport { Effect } from 'effect';\n\nimport { definePlugin, resource, type ResourceRef } from '../../api/define-plugin.ts';\nimport type { CodegenableDecl } from '../../contracts/codegenable.ts';\nimport type { SnapshotableDecl } from '../../contracts/snapshotable.ts';\nimport type { StrategyContributorDecl } from '../../contracts/strategy-contributor.ts';\nimport type { Contribution } from '../../substrate/plugin-ctx.ts';\nimport { emitContributions, PluginContext } from '../../substrate/plugin-ctx.ts';\nimport { CacheService } from '../../substrate/runtime/cache/index.ts';\nimport { suiResource } from '../sui/index.ts';\nimport type { SuiClient } from '../sui/index.ts';\nimport type { AccountFundingStrategy } from '../../contracts/funding-strategy.ts';\n\nimport { makeCoinCodegen, makeCoinStaticCodegen, type CoinBindings } from './codegen.ts';\nimport { makeCoinSnapshotable } from './snapshot.ts';\nimport { CoinRegistryService } from './registry.ts';\nimport { acquireCoin, type CoinAddressForm, type CoinValue } from './service.ts';\nimport { BUILTIN_COINS } from './address-resolution.ts';\nimport type { MetadataSdkShim } from './metadata.ts';\nimport type { MintSdkShim } from './mint.ts';\n\nexport const coinFundingCapabilityKey = <FullType extends string>(\n\tfullCoinType: FullType,\n): `coinType:${FullType}` => `coinType:${fullCoinType}` as const;\n\n// ---------------------------------------------------------------------------\n// Resource — one per declared coin instance, keyed by explicit address form.\n// ---------------------------------------------------------------------------\n\n/** Resource id constructor. The symbolic name is part of the resource identity\n * so the substrate's compose-time dedup catches collisions. */\nexport const coinResourceId = <Sym extends string>(symbol: Sym): `coin:${Sym}` => `coin:${symbol}`;\n\nexport type CoinResourceId<Sym extends string> = `coin:${Sym}`;\n\n/** Maximum length of the readable prefix in a `coin.known(...)` resource\n * id. Two long coin types sharing this prefix length would silently\n * collide in the substrate's compose-time dedup (string equality on\n * the resource id), so beyond this length we append a short hash of\n * the full coin type to disambiguate. */\nconst COIN_KNOWN_PREFIX_MAX = 60;\n\n/** Length of the SHA-256 hex suffix appended to long `coin.known(...)`\n * resource ids. Eight chars (~32 bits) is the same width used by other\n * collision-disambiguation hashes in the package (e.g. router profile,\n * fork preimage suffixes). */\nconst COIN_KNOWN_HASH_SUFFIX_LEN = 8;\n\n/** Derive a stable, human-readable, collision-free resource-id segment\n * for `coin.known(fullCoinType)`. Short types pass through as the\n * readable `<addr>_<module>_<witness>` form; long types get a hash\n * suffix so two types sharing a 60-char prefix can coexist. */\nconst coinKnownResourceKey = (fullCoinType: string): string => {\n\tconst readable = fullCoinType.replace(/^0x/, '').replace(/::/g, '_');\n\tif (readable.length <= COIN_KNOWN_PREFIX_MAX) {\n\t\treturn readable;\n\t}\n\tconst hash = createHash('sha256')\n\t\t.update(fullCoinType)\n\t\t.digest('hex')\n\t\t.slice(0, COIN_KNOWN_HASH_SUFFIX_LEN);\n\t// The readable prefix is truncated to leave room for the `_` divider\n\t// (`-1`) and the hash suffix (`-COIN_KNOWN_HASH_SUFFIX_LEN`), so the\n\t// resulting `<prefix>_<hash>` id has total length\n\t// ≤ COIN_KNOWN_PREFIX_MAX — bounded and deterministic per input.\n\treturn `${readable.slice(0, COIN_KNOWN_PREFIX_MAX - COIN_KNOWN_HASH_SUFFIX_LEN - 1)}_${hash}`;\n};\n\ntype PackageNameOf<Pkg extends PackageMember> =\n\tPkg extends ResourceRef<`package:${infer Name}`, PackageMemberValue> ? Name : string;\n\ntype PackageCoinResourceKey<\n\tPkg extends PackageMember,\n\tWit extends string,\n> = `${PackageNameOf<Pkg>}/${Lowercase<Wit>}`;\n\nconst packageNameFromMember = <Pkg extends PackageMember>(pkg: Pkg): PackageNameOf<Pkg> =>\n\tpkg.id.slice('package:'.length) as PackageNameOf<Pkg>;\n\n// ---------------------------------------------------------------------------\n// SDK shim projection\n// ---------------------------------------------------------------------------\n//\n// The coin plugin's resolvers need three Sui surfaces: `core.getObject`\n// (mint verify probe), `core.getCoinMetadata` (bare-type address form),\n// and the opaque `client` (`Transaction.build({client})` in the mint\n// produce body).\n//\n// `sui.sdk.core.getObject` lives on the typed `SuiSdkShim` directly;\n// `getCoinMetadata` lives on the underlying `ClientWithCoreApi['core']`\n// reached via `sui.sdk.client.core`. Project both onto the combined\n// `MetadataSdkShim & MintSdkShim` here at the boundary.\n\nconst projectCoinSdk = (sui: SuiClient): MetadataSdkShim & MintSdkShim => ({\n\tcore: {\n\t\tgetObject: sui.sdk.core.getObject,\n\t\tgetCoinMetadata: (args) => sui.sdk.client.core.getCoinMetadata(args),\n\t\tlistCoins: sui.sdk.core.listCoins,\n\t},\n\tclient: sui.sdk.client,\n\t// Fork mode mints offline with explicit gas (sui-fork has no simulate).\n\tforkMode: sui.fork !== null,\n});\n\n// ---------------------------------------------------------------------------\n// Per-form contribution decls — dynamic (POST-acquire). Receive the\n// resolved `CoinValue` so codegen bindings stamp the REAL fullCoinType\n// + decimals instead of placeholder values.\n//\n// `coinContributions` is a PURE decl-builder (NOT a routing wrapper): the\n// three coin `start` bodies feed its return into the shared\n// `emitContributions(ctx, …)`, which routes each decl by `kind`. Shared by\n// all three forms so the rich funding-strategy projection below lives once.\n// Decl shapes + ORDER are load-bearing.\n//\n// Exported for the funding-strategy decl-shape test: it feeds a hand-built\n// resolved `CoinValue` here and asserts the projected `strategy-contributor`\n// decl via `emitContributions` against a capturing ctx.\n// ---------------------------------------------------------------------------\n\nexport const coinContributions = (\n\tsymbol: string,\n\tresolved: CoinValue,\n): ReadonlyArray<Contribution> => {\n\tconst bindings: CoinBindings = {\n\t\tsymbol: resolved.symbol ?? symbol,\n\t\tfullCoinType: resolved.fullCoinType,\n\t\tdecimals: resolved.decimals,\n\t\tsource: resolved.source,\n\t\t...(resolved.displayName !== undefined ? { displayName: resolved.displayName } : {}),\n\t\t...(resolved.iconUrl !== undefined ? { iconUrl: resolved.iconUrl } : {}),\n\t\t...(resolved.treasuryCapId !== undefined ? { treasuryCapId: resolved.treasuryCapId } : {}),\n\t\t...(resolved.metadataId !== undefined ? { metadataId: resolved.metadataId } : {}),\n\t\t...(resolved.packageId !== undefined ? { packageId: resolved.packageId } : {}),\n\t};\n\tconst snap: SnapshotableDecl = makeCoinSnapshotable({ symbol });\n\tconst codegen: CodegenableDecl = makeCoinCodegen({\n\t\tsymbol,\n\t\tresolved: bindings,\n\t});\n\t// Project the coin's narrow `{address, amount}`-shaped strategy\n\t// to the wider cross-plugin `AccountFundingStrategy` contract\n\t// (`{address, amount, account}`) at the capability boundary. The\n\t// coin publisher signs the mint via its own lease (see\n\t// `coin/service.ts → mint`), so `account` is dropped honestly\n\t// here — the type-level contract is satisfied structurally\n\t// without a misleading `as` cast on the contribution literal.\n\t// Direct consumers (e.g. deepbook seed funding) keep using the\n\t// narrow shape on `CoinValue.fundingStrategy` directly.\n\tconst narrowStrategy = resolved.fundingStrategy;\n\tconst fundingContribution =\n\t\tnarrowStrategy === undefined\n\t\t\t? []\n\t\t\t: [\n\t\t\t\t\t{\n\t\t\t\t\t\tkind: 'strategy-contributor',\n\t\t\t\t\t\tcapabilityKey: coinFundingCapabilityKey(resolved.fullCoinType),\n\t\t\t\t\t\tstrategy: {\n\t\t\t\t\t\t\t// usesAccountSigner: true — the coin strategy mints via\n\t\t\t\t\t\t\t// the publisher account's own `withTransactionSigner`,\n\t\t\t\t\t\t\t// which acquires the per-address lease\n\t\t\t\t\t\t\t// `account:<publisherAddress>` internally (service.ts\n\t\t\t\t\t\t\t// mint -> performMint -> signAndDispatch). The account\n\t\t\t\t\t\t\t// funding dispatcher must therefore NOT wrap the request\n\t\t\t\t\t\t\t// in its own `account:<fundedAddress>` lease. Two reasons:\n\t\t\t\t\t\t\t// 1. The funded account is a passive `recipient` in\n\t\t\t\t\t\t\t// `mint_and_transfer` — it neither signs nor\n\t\t\t\t\t\t\t// contributes objects, so the funded-address lease\n\t\t\t\t\t\t\t// serializes nothing; the publisher lease already\n\t\t\t\t\t\t\t// serializes the only account whose gas + treasury\n\t\t\t\t\t\t\t// cap the mint consumes.\n\t\t\t\t\t\t\t// 2. When the funded address IS the publisher address\n\t\t\t\t\t\t\t// (fund a publisher with a coin it published), the\n\t\t\t\t\t\t\t// dispatcher's `account:<funded>` lease and the\n\t\t\t\t\t\t\t// mint's `account:<publisher>` lease collapse to the\n\t\t\t\t\t\t\t// same non-reentrant key, so the inner acquire would\n\t\t\t\t\t\t\t// block forever. Owning the lease inside the strategy\n\t\t\t\t\t\t\t// (signalled by this flag) makes self-funding\n\t\t\t\t\t\t\t// single-acquire and deadlock-free while leaving the\n\t\t\t\t\t\t\t// cross-account path's mint + publisher lease\n\t\t\t\t\t\t\t// unchanged.\n\t\t\t\t\t\t\t// Mirrors deepbook's DEEP strategy (faucet-strategy.ts),\n\t\t\t\t\t\t\t// which sets the same flag and self-acquires via\n\t\t\t\t\t\t\t// `req.account.withTransactionSigner`.\n\t\t\t\t\t\t\tusesAccountSigner: true,\n\t\t\t\t\t\t\trequest: (req) =>\n\t\t\t\t\t\t\t\tnarrowStrategy.request({ address: req.address, amount: req.amount }),\n\t\t\t\t\t\t} satisfies AccountFundingStrategy,\n\t\t\t\t\t\tautoMounted: true,\n\t\t\t\t\t} satisfies StrategyContributorDecl<`coinType:${string}`, AccountFundingStrategy>,\n\t\t\t\t];\n\treturn [snap, codegen, ...fundingContribution];\n};\n\n// ---------------------------------------------------------------------------\n// Form 1: coin.fromPackage(pkg, witness) — package-scoped registry lookup\n// ---------------------------------------------------------------------------\n\n/** A user-supplied package ref. The user passes the result of\n * `localPackage('foo', …)` / `knownPackage('foo', …)` — NOT a bare string\n * value. Generic over the literal package name so the witness-form\n * coin's dependency preserves the per-package resource id. */\nexport interface PackageMemberValue {\n\treadonly name: string;\n\treadonly packageId: string;\n\treadonly publisher?: import('./mint.ts').MintSigner;\n}\n\nexport type PackageMember<Name extends string = string> = ResourceRef<\n\t`package:${Name}`,\n\tPackageMemberValue\n>;\n\n/** Resolve a coin by `(publishing package member, witness)`. Forces\n * a dep edge on the publishing package's resource — the substrate ensures\n * the publish has completed before this resolves.\n *\n * Resource identity includes both the package's symbolic name and the\n * witness name, so two packages can expose the same witness without\n * colliding in the substrate graph.\n *\n * Pass the package MEMBER (the value returned by `localPackage(...)`\n * / `knownPackage(...)`). The factory\n * projects it to a dependency resource and receives the package value\n * in `start`. */\nexport const fromPackage = <const Pkg extends PackageMember, Wit extends string>(\n\tpkg: Pkg,\n\twitnessName: Wit,\n) => {\n\tconst packageName = packageNameFromMember(pkg);\n\tconst symbol = witnessName.toLowerCase() as Lowercase<Wit>;\n\tconst resourceKey = `${packageName}/${symbol}` as PackageCoinResourceKey<Pkg, Wit>;\n\tconst coinRef = resource<CoinResourceId<typeof resourceKey>, CoinValue>(\n\t\tcoinResourceId(resourceKey),\n\t);\n\treturn definePlugin({\n\t\tid: coinRef.id,\n\t\tdependsOn: { pkg, sui: suiResource },\n\t\trole: 'task',\n\t\t// Coin lifecycle is action-shaped — uses the action section bucket;\n\t\t// see substrate/projection.ts `RowSection` for the canonical list.\n\t\t// All three coin factories (`fromPackage` / `known` / `builtin`)\n\t\t// share this deliberate bucketing; promoting to a dedicated\n\t\t// `'coin'` section would ripple through every projection / TUI\n\t\t// consumer for marginal display value.\n\t\tsection: 'action',\n\t\t// Stack-free codegen: a package coin's type / decimals / package id\n\t\t// are LOADED CONFIG DATA -- the committed stub emits\n\t\t// `resolveValue('coin:<symbol>', '<key>')`, never a baked coin type.\n\t\tstaticCodegen: () => [makeCoinStaticCodegen({ symbol, source: 'registry' })],\n\t\t// `deps` auto-infers the resolved `{ pkg, sui }` dependency object;\n\t\t// `ctx` arrives via the `PluginContext` service.\n\t\tstart: ({ pkg: resolved, sui }) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst ctx = yield* PluginContext;\n\t\t\t\tconst artifactPublisher = yield* CacheService;\n\t\t\t\tconst registry = yield* CoinRegistryService;\n\t\t\t\tconst form: CoinAddressForm = {\n\t\t\t\t\tkind: 'witness',\n\t\t\t\t\tpublishingPackageName: resolved.name,\n\t\t\t\t\twitness: witnessName,\n\t\t\t\t\t...(resolved.publisher === undefined ? {} : { fundingSigner: resolved.publisher }),\n\t\t\t\t};\n\t\t\t\tconst value = yield* acquireCoin(form, {\n\t\t\t\t\tregistry,\n\t\t\t\t\tsdk: projectCoinSdk(sui),\n\t\t\t\t\tchainId: sui.chainId,\n\t\t\t\t\tpublisher: artifactPublisher,\n\t\t\t\t});\n\t\t\t\t// Emit the resolved coin's contributions inline. `value` is the\n\t\t\t\t// just-resolved `CoinValue`; `symbol` is the package-scoped\n\t\t\t\t// witness symbol in scope.\n\t\t\t\temitContributions(ctx, coinContributions(symbol, value));\n\t\t\t\treturn value;\n\t\t\t}),\n\t});\n};\n\n// ---------------------------------------------------------------------------\n// Form 2: coin.known(fullCoinType) — bare on-chain type\n// ---------------------------------------------------------------------------\n\n/** Resolve a coin by bare on-chain type via `getCoinMetadata`.\n * Used for live-net coins (mainnet DEEP, USDC etc.) that no local\n * `Package(...)` publishes.\n *\n * Soft-degrades to `decimals: 0` on RPC failure — distilled-doc\n * invariant. Resource id uses a deterministic-but-readable derivation of\n * the coin type so collisions surface at compose time. */\nexport const known = <FullType extends string>(fullCoinType: FullType) => {\n\t// Derive a resource id from the type: keep it readable but unique.\n\t// The substrate's compose-time dedup uses string equality on the id,\n\t// so two long coin types that share a 60-char prefix MUST NOT collide.\n\t// When the projection truncates, append a short hash of the FULL coin\n\t// type so the suffix disambiguates the two — the readable prefix\n\t// stays human-recognizable while the hash guarantees uniqueness.\n\tconst id = coinKnownResourceKey(fullCoinType);\n\tconst coinRef = resource<CoinResourceId<typeof id>, CoinValue>(\n\t\tcoinResourceId(id) as CoinResourceId<typeof id>,\n\t);\n\treturn definePlugin({\n\t\tid: coinRef.id,\n\t\tdependsOn: { sui: suiResource },\n\t\trole: 'task',\n\t\tsection: 'action',\n\t\t// Stack-free codegen: the caller DECLARED this on-chain coin type, so\n\t\t// the committed stub bakes `fullCoinType` as a LITERAL; `decimals` /\n\t\t// `packageId` still resolve at app build/dev time (they come from\n\t\t// `getCoinMetadata`, only known after a live probe).\n\t\tstaticCodegen: () => [\n\t\t\tmakeCoinStaticCodegen({ symbol: id, source: 'on-chain', knownCoinType: fullCoinType }),\n\t\t],\n\t\tstart: ({ sui }) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst ctx = yield* PluginContext;\n\t\t\t\tconst publisher = yield* CacheService;\n\t\t\t\tconst registry = yield* CoinRegistryService;\n\t\t\t\tconst form: CoinAddressForm = { kind: 'known', fullCoinType };\n\t\t\t\tconst value = yield* acquireCoin(form, {\n\t\t\t\t\tregistry,\n\t\t\t\t\tsdk: projectCoinSdk(sui),\n\t\t\t\t\tchainId: sui.chainId,\n\t\t\t\t\tpublisher,\n\t\t\t\t});\n\t\t\t\t// Emit inline. `value` is the resolved `CoinValue`; `id` is the\n\t\t\t\t// derived `coin.known` resource key.\n\t\t\t\temitContributions(ctx, coinContributions(id, value));\n\t\t\t\treturn value;\n\t\t\t}),\n\t});\n};\n\n// ---------------------------------------------------------------------------\n// Form 3: coin.builtin('sui') — protocol-defined constant\n// ---------------------------------------------------------------------------\n\n/** Resolve a protocol-defined builtin coin. Currently `'sui'` only —\n * distilled-doc 13-coin.md Invariant 4: SUI is `0x2::sui::SUI`,\n * decimals=9. No RPC, no registry. */\nexport const builtin = <Name extends keyof typeof BUILTIN_COINS>(name: Name) => {\n\tconst symbol = name; // 'sui' today\n\tconst coinRef = resource<CoinResourceId<Name>, CoinValue>(coinResourceId(name));\n\treturn definePlugin({\n\t\tid: coinRef.id,\n\t\tdependsOn: { sui: suiResource },\n\t\trole: 'task',\n\t\tsection: 'action',\n\t\t// Stack-free codegen: a builtin coin (SUI) is protocol-defined --\n\t\t// its coin type + decimals are constants baked as LITERALS (no\n\t\t// `resolveValue` that would throw at module load for a coin with no\n\t\t// injected id).\n\t\tstaticCodegen: () => [\n\t\t\tmakeCoinStaticCodegen({\n\t\t\t\tsymbol,\n\t\t\t\tsource: 'builtin',\n\t\t\t\tconstants: {\n\t\t\t\t\tfullCoinType: BUILTIN_COINS[name].fullCoinType,\n\t\t\t\t\tdecimals: BUILTIN_COINS[name].decimals,\n\t\t\t\t},\n\t\t\t}),\n\t\t],\n\t\tstart: ({ sui }) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst ctx = yield* PluginContext;\n\t\t\t\tconst publisher = yield* CacheService;\n\t\t\t\tconst registry = yield* CoinRegistryService;\n\t\t\t\tconst form: CoinAddressForm = { kind: 'builtin', name };\n\t\t\t\tconst value = yield* acquireCoin(form, {\n\t\t\t\t\tregistry,\n\t\t\t\t\tsdk: projectCoinSdk(sui),\n\t\t\t\t\tchainId: sui.chainId,\n\t\t\t\t\tpublisher,\n\t\t\t\t});\n\t\t\t\t// Emit inline. `value` is the resolved `CoinValue`; `symbol` is\n\t\t\t\t// the builtin coin name (`'sui'`).\n\t\t\t\temitContributions(ctx, coinContributions(symbol, value));\n\t\t\t\treturn value;\n\t\t\t}),\n\t});\n};\n\n// ---------------------------------------------------------------------------\n// Public `coin` namespace\n// ---------------------------------------------------------------------------\n\n/** User-facing factory namespace. Three variants — see file header for\n * the rationale on not exposing a `coin(identifier)` form-guessing\n * entry. */\nexport const coin = {\n\tfromPackage,\n\tknown,\n\tbuiltin,\n} as const;\n\n// ---------------------------------------------------------------------------\n// Re-exports — advanced callers (Wallet, Faucet, Deepbook, examples)\n// ---------------------------------------------------------------------------\n\nexport type { CoinValue, CoinAddressForm } from './service.ts';\nexport type { ResolvedCoin, BuiltinCoinName } from './address-resolution.ts';\nexport { BUILTIN_COINS } from './address-resolution.ts';\n\nexport type { CoinRecord, CoinRegistry, CoinKey } from './registry.ts';\nexport { CoinRegistryService, layerCoinRegistry } from './registry.ts';\n\nexport type { CoinBindings } from './codegen.ts';\n\nexport type { DiscoveredCoin } from './discovery.ts';\nexport { discoverCoinsFromPublish } from './discovery.ts';\n\nexport type { MetadataSdkShim, OnchainCoinMetadata, CoinMetadataCache } from './metadata.ts';\nexport {\n\tOnchainCoinMetadataShape,\n\tMETADATA_FETCH_TIMEOUT_MS,\n\tMETADATA_RETRY_SCHEDULE,\n\tfetchCoinMetadataOnce,\n\tfetchCoinMetadataMany,\n\tmakeCoinMetadataCache,\n\tisBareCoinType,\n\tvalidateBareCoinType,\n} from './metadata.ts';\n\nexport type { MintInputs, MintResult, MintSigner, MintSdkShim, CachedMint } from './mint.ts';\nexport { performMint, MintedCoinVerifyShape, mintTxError, mintParseError } from './mint.ts';\n\nexport type { CoinError, CoinPhase } from './errors.ts';\nexport { coinError } from './errors.ts';\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/plugins/coin/index.ts"],"sourcesContent":["// Coin plugin — barrel + `coin(...)` factory family.\n//\n// Architecture (13-coin.md): Coin is the user-facing primitive family\n// for *addressing* custom Move coin types. It does NOT publish Move\n// modules itself — the Package plugin publishes; coin auto-discovery\n// (in `discovery.ts`) folds the publish output into the per-stack\n// `CoinRegistry`; this factory resolves user-supplied addresses\n// (witness / bare-type / builtin) against that registry plus the live RPC.\n//\n// User-facing factory shape — three variants mirroring the address\n// forms:\n//\n// coin.fromPackage(pkg, 'MOCK_USDC') // package member → registry\n// coin.known('0x...::deep::DEEP') // bare → live RPC\n// coin.builtin('sui') // protocol-defined constant\n//\n// (A `coin(identifier)` convenience entry is intentionally NOT\n// exposed — distilled-doc 13-coin.md Pain point #6 documents how the\n// \"guess the form from the string\" path is a footgun. The three-form\n// surface forces the user to make the disambiguation explicit at the\n// call site.)\n//\n// Resource id: `'coin:<package>/<witness>'` for package-scoped coins and\n// `'coin:<identifier>'` for known/builtin coins — one tag per declared\n// coin instance, so the substrate's compose-time dedup detects collisions\n// cleanly. Mirrors the Package plugin's per-instance resource identity.\n\nimport { createHash } from 'node:crypto';\n\nimport { Effect } from 'effect';\n\nimport { definePlugin, resource, type ResourceRef } from '../../api/define-plugin.ts';\nimport type { CodegenableDecl } from '../../contracts/codegenable.ts';\nimport type { SnapshotableDecl } from '../../contracts/snapshotable.ts';\nimport type { StrategyContributorDecl } from '../../contracts/strategy-contributor.ts';\nimport type { Contribution } from '../../substrate/plugin-ctx.ts';\nimport { emitContributions, PluginContext } from '../../substrate/plugin-ctx.ts';\nimport { CacheService } from '../../substrate/runtime/cache/index.ts';\nimport { suiResource } from '../sui/index.ts';\nimport type { SuiClient } from '../sui/index.ts';\nimport type { AccountFundingStrategy } from '../../contracts/funding-strategy.ts';\n\nimport { makeCoinCodegen, makeCoinStaticCodegen, type CoinBindings } from './codegen.ts';\nimport { makeCoinSnapshotable } from './snapshot.ts';\nimport { CoinRegistryService } from './registry.ts';\nimport { acquireCoin, type CoinAddressForm, type CoinValue } from './service.ts';\nimport { BUILTIN_COINS } from './address-resolution.ts';\nimport type { MetadataSdkShim } from './metadata.ts';\nimport type { MintSdkShim } from './mint.ts';\n\nexport const coinFundingCapabilityKey = <FullType extends string>(\n\tfullCoinType: FullType,\n): `coinType:${FullType}` => `coinType:${fullCoinType}` as const;\n\n// ---------------------------------------------------------------------------\n// Resource — one per declared coin instance, keyed by explicit address form.\n// ---------------------------------------------------------------------------\n\n/** Resource id constructor. The symbolic name is part of the resource identity\n * so the substrate's compose-time dedup catches collisions. */\nexport const coinResourceId = <Sym extends string>(symbol: Sym): `coin:${Sym}` => `coin:${symbol}`;\n\nexport type CoinResourceId<Sym extends string> = `coin:${Sym}`;\n\n/** Maximum length of the readable prefix in a `coin.known(...)` resource\n * id. Two long coin types sharing this prefix length would silently\n * collide in the substrate's compose-time dedup (string equality on\n * the resource id), so beyond this length we append a short hash of\n * the full coin type to disambiguate. */\nconst COIN_KNOWN_PREFIX_MAX = 60;\n\n/** Length of the SHA-256 hex suffix appended to long `coin.known(...)`\n * resource ids. Eight chars (~32 bits) is the same width used by other\n * collision-disambiguation hashes in the package (e.g. router profile,\n * fork preimage suffixes). */\nconst COIN_KNOWN_HASH_SUFFIX_LEN = 8;\n\n/** Derive a stable, human-readable, collision-free resource-id segment\n * for `coin.known(fullCoinType)`. Short types pass through as the\n * readable `<addr>_<module>_<witness>` form; long types get a hash\n * suffix so two types sharing a 60-char prefix can coexist. */\nconst coinKnownResourceKey = (fullCoinType: string): string => {\n\tconst readable = fullCoinType.replace(/^0x/, '').replace(/::/g, '_');\n\tif (readable.length <= COIN_KNOWN_PREFIX_MAX) {\n\t\treturn readable;\n\t}\n\tconst hash = createHash('sha256')\n\t\t.update(fullCoinType)\n\t\t.digest('hex')\n\t\t.slice(0, COIN_KNOWN_HASH_SUFFIX_LEN);\n\t// The readable prefix is truncated to leave room for the `_` divider\n\t// (`-1`) and the hash suffix (`-COIN_KNOWN_HASH_SUFFIX_LEN`), so the\n\t// resulting `<prefix>_<hash>` id has total length\n\t// ≤ COIN_KNOWN_PREFIX_MAX — bounded and deterministic per input.\n\treturn `${readable.slice(0, COIN_KNOWN_PREFIX_MAX - COIN_KNOWN_HASH_SUFFIX_LEN - 1)}_${hash}`;\n};\n\ntype PackageNameOf<Pkg extends PackageMember> =\n\tPkg extends ResourceRef<`package:${infer Name}`, PackageMemberValue> ? Name : string;\n\ntype PackageCoinResourceKey<\n\tPkg extends PackageMember,\n\tWit extends string,\n> = `${PackageNameOf<Pkg>}/${Lowercase<Wit>}`;\n\nconst packageNameFromMember = <Pkg extends PackageMember>(pkg: Pkg): PackageNameOf<Pkg> =>\n\tpkg.id.slice('package:'.length) as PackageNameOf<Pkg>;\n\n// ---------------------------------------------------------------------------\n// SDK shim projection\n// ---------------------------------------------------------------------------\n//\n// The coin plugin's resolvers need three Sui surfaces: `core.getObject`\n// (mint verify probe), `core.getCoinMetadata` (bare-type address form),\n// and the opaque `client` (`Transaction.build({client})` in the mint\n// produce body).\n//\n// `sui.sdk.core.getObject` lives on the typed `SuiSdkShim` directly;\n// `getCoinMetadata` lives on the underlying `ClientWithCoreApi['core']`\n// reached via `sui.sdk.client.core`. Project both onto the combined\n// `MetadataSdkShim & MintSdkShim` here at the boundary.\n\nconst projectCoinSdk = (sui: SuiClient): MetadataSdkShim & MintSdkShim => ({\n\tcore: {\n\t\tgetObject: sui.sdk.core.getObject,\n\t\tgetCoinMetadata: (args) => sui.sdk.client.core.getCoinMetadata(args),\n\t\tlistCoins: sui.sdk.core.listCoins,\n\t},\n\tclient: sui.sdk.client,\n\t// Fork mode mints offline with explicit gas (sui-fork has no simulate).\n\tforkMode: sui.fork !== null,\n});\n\n// ---------------------------------------------------------------------------\n// Per-form contribution decls — dynamic (POST-acquire). Receive the\n// resolved `CoinValue` so codegen bindings stamp the REAL fullCoinType\n// + decimals instead of placeholder values.\n//\n// `coinContributions` is a PURE decl-builder (NOT a routing wrapper): the\n// three coin `start` bodies feed its return into the shared\n// `emitContributions(ctx, …)`, which routes each decl by `kind`. Shared by\n// all three forms so the rich funding-strategy projection below lives once.\n// Decl shapes + ORDER are load-bearing.\n//\n// Exported for the funding-strategy decl-shape test: it feeds a hand-built\n// resolved `CoinValue` here and asserts the projected `strategy-contributor`\n// decl via `emitContributions` against a capturing ctx.\n// ---------------------------------------------------------------------------\n\nexport const coinContributions = (\n\tsymbol: string,\n\tresolved: CoinValue,\n): ReadonlyArray<Contribution> => {\n\tconst bindings: CoinBindings = {\n\t\tsymbol: resolved.symbol ?? symbol,\n\t\tfullCoinType: resolved.fullCoinType,\n\t\tdecimals: resolved.decimals,\n\t\tsource: resolved.source,\n\t\t...(resolved.displayName !== undefined ? { displayName: resolved.displayName } : {}),\n\t\t...(resolved.iconUrl !== undefined ? { iconUrl: resolved.iconUrl } : {}),\n\t\t...(resolved.treasuryCapId !== undefined ? { treasuryCapId: resolved.treasuryCapId } : {}),\n\t\t...(resolved.metadataId !== undefined ? { metadataId: resolved.metadataId } : {}),\n\t\t...(resolved.packageId !== undefined ? { packageId: resolved.packageId } : {}),\n\t};\n\tconst snap: SnapshotableDecl = makeCoinSnapshotable({ symbol });\n\tconst codegen: CodegenableDecl = makeCoinCodegen({\n\t\tsymbol,\n\t\tresolved: bindings,\n\t});\n\t// Project the coin's narrow `{address, amount}`-shaped strategy\n\t// to the wider cross-plugin `AccountFundingStrategy` contract\n\t// (`{address, amount, account}`) at the capability boundary. The\n\t// coin publisher signs the mint via its own lease (see\n\t// `coin/service.ts → mint`), so `account` is dropped honestly\n\t// here — the type-level contract is satisfied structurally\n\t// without a misleading `as` cast on the contribution literal.\n\t// Direct consumers (e.g. deepbook seed funding) keep using the\n\t// narrow shape on `CoinValue.fundingStrategy` directly.\n\tconst narrowStrategy = resolved.fundingStrategy;\n\tconst fundingContribution =\n\t\tnarrowStrategy === undefined\n\t\t\t? []\n\t\t\t: [\n\t\t\t\t\t{\n\t\t\t\t\t\tkind: 'strategy-contributor',\n\t\t\t\t\t\tcapabilityKey: coinFundingCapabilityKey(resolved.fullCoinType),\n\t\t\t\t\t\tstrategy: {\n\t\t\t\t\t\t\t// usesAccountSigner: true — the coin strategy mints via\n\t\t\t\t\t\t\t// the publisher account's own `withTransactionSigner`,\n\t\t\t\t\t\t\t// which acquires the per-address lease\n\t\t\t\t\t\t\t// `account:<publisherAddress>` internally (service.ts\n\t\t\t\t\t\t\t// mint -> performMint -> signAndDispatch). The account\n\t\t\t\t\t\t\t// funding dispatcher must therefore NOT wrap the request\n\t\t\t\t\t\t\t// in its own `account:<fundedAddress>` lease. Two reasons:\n\t\t\t\t\t\t\t// 1. The funded account is a passive `recipient` in\n\t\t\t\t\t\t\t// `mint_and_transfer` — it neither signs nor\n\t\t\t\t\t\t\t// contributes objects, so the funded-address lease\n\t\t\t\t\t\t\t// serializes nothing; the publisher lease already\n\t\t\t\t\t\t\t// serializes the only account whose gas + treasury\n\t\t\t\t\t\t\t// cap the mint consumes.\n\t\t\t\t\t\t\t// 2. When the funded address IS the publisher address\n\t\t\t\t\t\t\t// (fund a publisher with a coin it published), the\n\t\t\t\t\t\t\t// dispatcher's `account:<funded>` lease and the\n\t\t\t\t\t\t\t// mint's `account:<publisher>` lease collapse to the\n\t\t\t\t\t\t\t// same non-reentrant key, so the inner acquire would\n\t\t\t\t\t\t\t// block forever. Owning the lease inside the strategy\n\t\t\t\t\t\t\t// (signalled by this flag) makes self-funding\n\t\t\t\t\t\t\t// single-acquire and deadlock-free while leaving the\n\t\t\t\t\t\t\t// cross-account path's mint + publisher lease\n\t\t\t\t\t\t\t// unchanged.\n\t\t\t\t\t\t\t// Mirrors deepbook's DEEP strategy (faucet-strategy.ts),\n\t\t\t\t\t\t\t// which sets the same flag and self-acquires via\n\t\t\t\t\t\t\t// `req.account.withTransactionSigner`.\n\t\t\t\t\t\t\tusesAccountSigner: true,\n\t\t\t\t\t\t\trequest: (req) =>\n\t\t\t\t\t\t\t\tnarrowStrategy.request({ address: req.address, amount: req.amount }),\n\t\t\t\t\t\t} satisfies AccountFundingStrategy,\n\t\t\t\t\t\tautoMounted: true,\n\t\t\t\t\t} satisfies StrategyContributorDecl<`coinType:${string}`, AccountFundingStrategy>,\n\t\t\t\t];\n\treturn [snap, codegen, ...fundingContribution];\n};\n\n// ---------------------------------------------------------------------------\n// Form 1: coin.fromPackage(pkg, witness) — package-scoped registry lookup\n// ---------------------------------------------------------------------------\n\n/** A user-supplied package ref. The user passes the result of\n * `localPackage('foo', …)` / `knownPackage('foo', …)` — NOT a bare string\n * value. Generic over the literal package name so the witness-form\n * coin's dependency preserves the per-package resource id. */\nexport interface PackageMemberValue {\n\treadonly name: string;\n\treadonly packageId: string;\n\treadonly publisher?: import('./mint.ts').MintSigner;\n}\n\nexport type PackageMember<Name extends string = string> = ResourceRef<\n\t`package:${Name}`,\n\tPackageMemberValue\n>;\n\n/** Resolve a coin by `(publishing package member, witness)`. Forces\n * a dep edge on the publishing package's resource — the substrate ensures\n * the publish has completed before this resolves.\n *\n * Resource identity includes both the package's symbolic name and the\n * witness name, so two packages can expose the same witness without\n * colliding in the substrate graph.\n *\n * Pass the package MEMBER (the value returned by `localPackage(...)`\n * / `knownPackage(...)`). The factory\n * projects it to a dependency resource and receives the package value\n * in `start`. */\nexport const fromPackage = <const Pkg extends PackageMember, Wit extends string>(\n\tpkg: Pkg,\n\twitnessName: Wit,\n) => {\n\tconst packageName = packageNameFromMember(pkg);\n\tconst symbol = witnessName.toLowerCase() as Lowercase<Wit>;\n\tconst resourceKey = `${packageName}/${symbol}` as PackageCoinResourceKey<Pkg, Wit>;\n\tconst coinRef = resource<CoinResourceId<typeof resourceKey>, CoinValue>(\n\t\tcoinResourceId(resourceKey),\n\t);\n\treturn definePlugin({\n\t\tid: coinRef.id,\n\t\tdependsOn: { pkg, sui: suiResource },\n\t\trole: 'task',\n\t\t// Coin lifecycle is action-shaped — uses the action section bucket;\n\t\t// see substrate/projection.ts `RowSection` for the canonical list.\n\t\t// All three coin factories (`fromPackage` / `known` / `builtin`)\n\t\t// share this deliberate bucketing; promoting to a dedicated\n\t\t// `'coin'` section would ripple through every projection / TUI\n\t\t// consumer for marginal display value.\n\t\tsection: 'action',\n\t\t// Stack-free codegen: a package coin's type / decimals / package id\n\t\t// are LOADED CONFIG DATA -- the committed stub emits\n\t\t// `requireValue(dep, 'coin:<symbol>', '<key>')`, never a baked coin type.\n\t\tstaticCodegen: () => [makeCoinStaticCodegen({ symbol, source: 'registry' })],\n\t\t// `deps` auto-infers the resolved `{ pkg, sui }` dependency object;\n\t\t// `ctx` arrives via the `PluginContext` service.\n\t\tstart: ({ pkg: resolved, sui }) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst ctx = yield* PluginContext;\n\t\t\t\tconst artifactPublisher = yield* CacheService;\n\t\t\t\tconst registry = yield* CoinRegistryService;\n\t\t\t\tconst form: CoinAddressForm = {\n\t\t\t\t\tkind: 'witness',\n\t\t\t\t\tpublishingPackageName: resolved.name,\n\t\t\t\t\twitness: witnessName,\n\t\t\t\t\t...(resolved.publisher === undefined ? {} : { fundingSigner: resolved.publisher }),\n\t\t\t\t};\n\t\t\t\tconst value = yield* acquireCoin(form, {\n\t\t\t\t\tregistry,\n\t\t\t\t\tsdk: projectCoinSdk(sui),\n\t\t\t\t\tchainId: sui.chainId,\n\t\t\t\t\tpublisher: artifactPublisher,\n\t\t\t\t});\n\t\t\t\t// Emit the resolved coin's contributions inline. `value` is the\n\t\t\t\t// just-resolved `CoinValue`; `symbol` is the package-scoped\n\t\t\t\t// witness symbol in scope.\n\t\t\t\temitContributions(ctx, coinContributions(symbol, value));\n\t\t\t\treturn value;\n\t\t\t}),\n\t});\n};\n\n// ---------------------------------------------------------------------------\n// Form 2: coin.known(fullCoinType) — bare on-chain type\n// ---------------------------------------------------------------------------\n\n/** Resolve a coin by bare on-chain type via `getCoinMetadata`.\n * Used for live-net coins (mainnet DEEP, USDC etc.) that no local\n * `Package(...)` publishes.\n *\n * Soft-degrades to `decimals: 0` on RPC failure — distilled-doc\n * invariant. Resource id uses a deterministic-but-readable derivation of\n * the coin type so collisions surface at compose time. */\nexport const known = <FullType extends string>(fullCoinType: FullType) => {\n\t// Derive a resource id from the type: keep it readable but unique.\n\t// The substrate's compose-time dedup uses string equality on the id,\n\t// so two long coin types that share a 60-char prefix MUST NOT collide.\n\t// When the projection truncates, append a short hash of the FULL coin\n\t// type so the suffix disambiguates the two — the readable prefix\n\t// stays human-recognizable while the hash guarantees uniqueness.\n\tconst id = coinKnownResourceKey(fullCoinType);\n\tconst coinRef = resource<CoinResourceId<typeof id>, CoinValue>(\n\t\tcoinResourceId(id) as CoinResourceId<typeof id>,\n\t);\n\treturn definePlugin({\n\t\tid: coinRef.id,\n\t\tdependsOn: { sui: suiResource },\n\t\trole: 'task',\n\t\tsection: 'action',\n\t\t// Stack-free codegen: the caller DECLARED this on-chain coin type, so\n\t\t// the committed stub bakes `fullCoinType` as a LITERAL; `decimals` /\n\t\t// `packageId` still resolve at app build/dev time (they come from\n\t\t// `getCoinMetadata`, only known after a live probe).\n\t\tstaticCodegen: () => [\n\t\t\tmakeCoinStaticCodegen({ symbol: id, source: 'on-chain', knownCoinType: fullCoinType }),\n\t\t],\n\t\tstart: ({ sui }) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst ctx = yield* PluginContext;\n\t\t\t\tconst publisher = yield* CacheService;\n\t\t\t\tconst registry = yield* CoinRegistryService;\n\t\t\t\tconst form: CoinAddressForm = { kind: 'known', fullCoinType };\n\t\t\t\tconst value = yield* acquireCoin(form, {\n\t\t\t\t\tregistry,\n\t\t\t\t\tsdk: projectCoinSdk(sui),\n\t\t\t\t\tchainId: sui.chainId,\n\t\t\t\t\tpublisher,\n\t\t\t\t});\n\t\t\t\t// Emit inline. `value` is the resolved `CoinValue`; `id` is the\n\t\t\t\t// derived `coin.known` resource key.\n\t\t\t\temitContributions(ctx, coinContributions(id, value));\n\t\t\t\treturn value;\n\t\t\t}),\n\t});\n};\n\n// ---------------------------------------------------------------------------\n// Form 3: coin.builtin('sui') — protocol-defined constant\n// ---------------------------------------------------------------------------\n\n/** Resolve a protocol-defined builtin coin. Currently `'sui'` only —\n * distilled-doc 13-coin.md Invariant 4: SUI is `0x2::sui::SUI`,\n * decimals=9. No RPC, no registry. */\nexport const builtin = <Name extends keyof typeof BUILTIN_COINS>(name: Name) => {\n\tconst symbol = name; // 'sui' today\n\tconst coinRef = resource<CoinResourceId<Name>, CoinValue>(coinResourceId(name));\n\treturn definePlugin({\n\t\tid: coinRef.id,\n\t\tdependsOn: { sui: suiResource },\n\t\trole: 'task',\n\t\tsection: 'action',\n\t\t// Stack-free codegen: a builtin coin (SUI) is protocol-defined --\n\t\t// its coin type + decimals are constants baked as LITERALS (no\n\t\t// `requireValue` that would throw at module load for a coin with no\n\t\t// injected id).\n\t\tstaticCodegen: () => [\n\t\t\tmakeCoinStaticCodegen({\n\t\t\t\tsymbol,\n\t\t\t\tsource: 'builtin',\n\t\t\t\tconstants: {\n\t\t\t\t\tfullCoinType: BUILTIN_COINS[name].fullCoinType,\n\t\t\t\t\tdecimals: BUILTIN_COINS[name].decimals,\n\t\t\t\t},\n\t\t\t}),\n\t\t],\n\t\tstart: ({ sui }) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst ctx = yield* PluginContext;\n\t\t\t\tconst publisher = yield* CacheService;\n\t\t\t\tconst registry = yield* CoinRegistryService;\n\t\t\t\tconst form: CoinAddressForm = { kind: 'builtin', name };\n\t\t\t\tconst value = yield* acquireCoin(form, {\n\t\t\t\t\tregistry,\n\t\t\t\t\tsdk: projectCoinSdk(sui),\n\t\t\t\t\tchainId: sui.chainId,\n\t\t\t\t\tpublisher,\n\t\t\t\t});\n\t\t\t\t// Emit inline. `value` is the resolved `CoinValue`; `symbol` is\n\t\t\t\t// the builtin coin name (`'sui'`).\n\t\t\t\temitContributions(ctx, coinContributions(symbol, value));\n\t\t\t\treturn value;\n\t\t\t}),\n\t});\n};\n\n// ---------------------------------------------------------------------------\n// Public `coin` namespace\n// ---------------------------------------------------------------------------\n\n/** User-facing factory namespace. Three variants — see file header for\n * the rationale on not exposing a `coin(identifier)` form-guessing\n * entry. */\nexport const coin = {\n\tfromPackage,\n\tknown,\n\tbuiltin,\n} as const;\n\n// ---------------------------------------------------------------------------\n// Re-exports — advanced callers (Wallet, Faucet, Deepbook, examples)\n// ---------------------------------------------------------------------------\n\nexport type { CoinValue, CoinAddressForm } from './service.ts';\nexport type { ResolvedCoin, BuiltinCoinName } from './address-resolution.ts';\nexport { BUILTIN_COINS } from './address-resolution.ts';\n\nexport type { CoinRecord, CoinRegistry, CoinKey } from './registry.ts';\nexport { CoinRegistryService, layerCoinRegistry } from './registry.ts';\n\nexport type { CoinBindings } from './codegen.ts';\n\nexport type { DiscoveredCoin } from './discovery.ts';\nexport { discoverCoinsFromPublish } from './discovery.ts';\n\nexport type { MetadataSdkShim, OnchainCoinMetadata, CoinMetadataCache } from './metadata.ts';\nexport {\n\tOnchainCoinMetadataShape,\n\tMETADATA_FETCH_TIMEOUT_MS,\n\tMETADATA_RETRY_SCHEDULE,\n\tfetchCoinMetadataOnce,\n\tfetchCoinMetadataMany,\n\tmakeCoinMetadataCache,\n\tisBareCoinType,\n\tvalidateBareCoinType,\n} from './metadata.ts';\n\nexport type { MintInputs, MintResult, MintSigner, MintSdkShim, CachedMint } from './mint.ts';\nexport { performMint, MintedCoinVerifyShape, mintTxError, mintParseError } from './mint.ts';\n\nexport type { CoinError, CoinPhase } from './errors.ts';\nexport { coinError } from './errors.ts';\n"],"mappings":"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"}
@@ -89,6 +89,17 @@ const narrowEnum = (raw, allowed, field, faults, fallback) => {
89
89
  faults.push(raw === void 0 || raw === null ? `${field}: missing (expected one of ${allowed.join("|")})` : `${field}: '${String(raw)}' not in ${allowed.join("|")}`);
90
90
  return fallback;
91
91
  };
92
+ /** Required string-or-numeric coerced to a string. Pyth feed prices arrive as a
93
+ * `bigint` on the resolved value (string in the cached/known shape); accept any
94
+ * of string/number/bigint and record a fault otherwise. Returns '0' on
95
+ * mismatch so the panel still renders a (degraded) cell. */
96
+ const reqNumericStr = (v, field, faults) => {
97
+ if (typeof v === "string") return v;
98
+ if (typeof v === "bigint") return v.toString();
99
+ if (typeof v === "number" && Number.isFinite(v)) return String(v);
100
+ faults.push(typeFault(field, "string|number|bigint", v));
101
+ return "0";
102
+ };
92
103
  const optStr = (v) => typeof v === "string" ? v : null;
93
104
  const optNum = (v) => typeof v === "number" && Number.isFinite(v) ? v : null;
94
105
  const bool = (v) => v === true;
@@ -132,7 +143,13 @@ const buildDashboardDomain = (deps) => {
132
143
  baseCoinType: reqStr(p.baseCoinType, `deepbook.pools[${i}].baseCoinType`, faults),
133
144
  quoteCoinType: reqStr(p.quoteCoinType, `deepbook.pools[${i}].quoteCoinType`, faults)
134
145
  })),
135
- marketMakerRunning: bool(v.marketMakerRunning),
146
+ pythFeeds: (v.pyth?.feeds ?? []).map((f, i) => ({
147
+ symbol: reqStr(f.symbol, `deepbook.pyth.feeds[${i}].symbol`, faults),
148
+ priceInfoObjectId: reqStr(f.priceInfoObjectId, `deepbook.pyth.feeds[${i}].priceInfoObjectId`, faults),
149
+ price: reqNumericStr(f.price, `deepbook.pyth.feeds[${i}].price`, faults),
150
+ expo: reqNum(f.expo, `deepbook.pyth.feeds[${i}].expo`, faults)
151
+ })),
152
+ hasSeedLiquidity: bool(v.hasSeedLiquidity),
136
153
  serverUrl: optStr(v.serverUrl),
137
154
  indexerUrl: optStr(v.indexerUrl),
138
155
  narrowingFault: faultDetail(faults)