@mysten-incubation/dev-wallet 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/dist/adapters/build-managed-account.d.mts +15 -2
  3. package/dist/adapters/build-managed-account.d.mts.map +1 -1
  4. package/dist/adapters/build-managed-account.mjs +28 -4
  5. package/dist/adapters/build-managed-account.mjs.map +1 -1
  6. package/dist/adapters/devstack-adapter.d.mts +19 -7
  7. package/dist/adapters/devstack-adapter.d.mts.map +1 -1
  8. package/dist/adapters/devstack-adapter.mjs +16 -8
  9. package/dist/adapters/devstack-adapter.mjs.map +1 -1
  10. package/dist/inject/index.d.mts +37 -16
  11. package/dist/inject/index.d.mts.map +1 -1
  12. package/dist/inject/index.mjs +20 -6
  13. package/dist/inject/index.mjs.map +1 -1
  14. package/dist/standalone/assets/{base-adapter-DET6CP7z.js → base-adapter-C5w5q7XZ.js} +1 -1
  15. package/dist/standalone/assets/{in-memory-adapter-CX7xym4e.js → in-memory-adapter-Ban2kRYS.js} +1 -1
  16. package/dist/standalone/assets/{main-CuOnBmaa.js → main-xfzarPod.js} +3 -3
  17. package/dist/standalone/assets/{webcrypto-adapter-CUQmbHDy.js → webcrypto-adapter-Ddqn-RAu.js} +1 -1
  18. package/dist/standalone/index.html +2 -2
  19. package/dist/ui/dev-wallet-balances.d.mts +7 -0
  20. package/dist/ui/dev-wallet-balances.d.mts.map +1 -1
  21. package/dist/ui/dev-wallet-balances.mjs +50 -3
  22. package/dist/ui/dev-wallet-balances.mjs.map +1 -1
  23. package/dist/ui/wallet-controller.mjs +2 -1
  24. package/dist/ui/wallet-controller.mjs.map +1 -1
  25. package/dist/wallet/dev-wallet.d.mts +26 -2
  26. package/dist/wallet/dev-wallet.d.mts.map +1 -1
  27. package/dist/wallet/dev-wallet.mjs +35 -6
  28. package/dist/wallet/dev-wallet.mjs.map +1 -1
  29. package/package.json +1 -1
  30. package/src/adapters/build-managed-account.ts +28 -2
  31. package/src/adapters/devstack-adapter.ts +28 -8
  32. package/src/inject/index.ts +98 -33
  33. package/src/ui/dev-wallet-balances.ts +62 -3
  34. package/src/ui/wallet-controller.ts +2 -1
  35. package/src/wallet/dev-wallet.ts +47 -7
package/CHANGELOG.md CHANGED
@@ -1,5 +1,48 @@
1
1
  # @mysten-incubation/dev-wallet
2
2
 
3
+ ## 0.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - efa3ab6: Multi-network deployment config.
8
+
9
+ - **devstack**: renamed the persisted `id-config`/`ids` layer to `deployment`
10
+ (`NetworkDeployment`/`DevstackDeployment`, `loadDeployment`, `dump-deployment`). Added
11
+ multi-network support — per-network service buckets via `forNetwork(network)`, typed committed
12
+ `deployments/<net>.ts` files (`dump-deployment --network <net>`), and a deployment envelope
13
+ merged at the Vite layer (committed networks for a prod build, the live local stack overlaid on
14
+ `vite dev`). Removed the legacy `resolve*()` config-runtime shims and the
15
+ `@devstack-dev/generated-extras` subsystem.
16
+ - **dev-wallet**: the injected dev wallet now operates across the full network set — it advertises
17
+ every configured network as the wallet-standard chain `sui:<name>` (including fork/custom
18
+ names), routes a per-network faucet, and stays registered across a dApp Kit `switchNetwork`.
19
+ `createDevstackAdapterFromManifest` accepts an optional `networks` set so manifest-built
20
+ adapters advertise the same chains.
21
+ - **create-devstack-app**: templates reshaped around the deployment API and multi-network
22
+ deployments (typed `deployments/<net>.ts` authoring surface, dApp Kit wired to the generated
23
+ network set).
24
+
25
+ ## 0.4.1
26
+
27
+ ### Patch Changes
28
+
29
+ - d3d3805: Fix scaffolded-app build/dev breakages and dashboard reporting; reshape codegen + tests.
30
+
31
+ - **create-devstack-app**: declare `lit` + `@mysten/signers` (fix `vite build` "failed to resolve
32
+ lit" and the dev-wallet injection crash); run `pnpm codegen` after install when `sui` is on PATH
33
+ (`--no-codegen` to skip); move tests to `tests/unit` · `tests/e2e` · `tests/browser` with a
34
+ standalone `tsconfig.test.json`.
35
+ - **devstack**: the Vite plugin now dedupes only Lit packages hoisted at the app root (phantom
36
+ packages no longer break the production build); `devstack codegen` requires a host `sui` CLI
37
+ (the Docker fallback is removed) and fails fast when it's missing; the vitest/Playwright presets
38
+ adopt the `tests/unit`/`tests/e2e`/`tests/browser` layout; the dashboard surfaces Pyth price
39
+ feeds (`DeepbookInfo.pythFeeds`) and renames `marketMakerRunning` → `hasSeedLiquidity`; fix a
40
+ bug where `devstack up`'s extras emit clobbered the committed `src/generated/.gitignore` with an
41
+ ignore-all policy.
42
+ - **dev-wallet**: the WebCrypto adapter is loaded lazily and gated on the optional
43
+ `@mysten/signers` peer, so an app without it still gets a working dev wallet instead of a hard
44
+ inject crash.
45
+
3
46
  ## 0.4.0
4
47
 
5
48
  ### Minor Changes
@@ -4,9 +4,22 @@ import { Signer } from "@mysten/sui/cryptography";
4
4
 
5
5
  //#region src/adapters/build-managed-account.d.ts
6
6
  /**
7
- * Build a {@link ManagedAccount} from a signer, address, label, and optional feature list.
7
+ * Resolve the wallet-standard chains to advertise for the given configured
8
+ * network names. Each network is advertised as `sui:<name>` so dApp Kit's
9
+ * chain-gated paths work for fork networks (`testnet-fork`, …) and custom
10
+ * names, unioned with the standard {@link SUI_CHAINS} for safety. Standard
11
+ * chains come first; configured-only chains are appended in the given order,
12
+ * de-duplicated.
8
13
  */
9
- declare function buildManagedAccount(signer: Signer, address: string, label: string, features?: readonly IdentifierString[]): ManagedAccount;
14
+ declare function chainsForNetworks(networks?: readonly string[]): `sui:${string}`[];
15
+ /**
16
+ * Build a {@link ManagedAccount} from a signer, address, label, optional
17
+ * feature list, and optional advertised chains. When `chains` is omitted the
18
+ * account advertises the standard {@link SUI_CHAINS}; pass the configured
19
+ * network names (via {@link chainsForNetworks}) so per-account advertised
20
+ * chains include fork/custom networks too.
21
+ */
22
+ declare function buildManagedAccount(signer: Signer, address: string, label: string, features?: readonly IdentifierString[], chains?: readonly `sui:${string}`[]): ManagedAccount;
10
23
  //#endregion
11
24
  export { buildManagedAccount };
12
25
  //# sourceMappingURL=build-managed-account.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"build-managed-account.d.mts","names":[],"sources":["../../src/adapters/build-managed-account.ts"],"mappings":";;;;;;;AAkBA;iBAAgB,mBAAA,CACf,MAAA,EAAQ,MAAA,EACR,OAAA,UACA,KAAA,UACA,QAAA,YAAoB,gBAAA,KAClB,cAAA"}
1
+ {"version":3,"file":"build-managed-account.d.mts","names":[],"sources":["../../src/adapters/build-managed-account.ts"],"mappings":";;;;;;;AAuBA;;;;AAA8D;AAoB9D;iBApBgB,iBAAA,CAAkB,QAA4B;;;;;;;;iBAoB9C,mBAAA,CACf,MAAA,EAAQ,MAAA,EACR,OAAA,UACA,KAAA,UACA,QAAA,YAAoB,gBAAA,IACpB,MAAA,gCACE,cAAA"}
@@ -6,9 +6,33 @@ const DEFAULT_FEATURES = [
6
6
  "sui:signPersonalMessage"
7
7
  ];
8
8
  /**
9
- * Build a {@link ManagedAccount} from a signer, address, label, and optional feature list.
9
+ * Resolve the wallet-standard chains to advertise for the given configured
10
+ * network names. Each network is advertised as `sui:<name>` so dApp Kit's
11
+ * chain-gated paths work for fork networks (`testnet-fork`, …) and custom
12
+ * names, unioned with the standard {@link SUI_CHAINS} for safety. Standard
13
+ * chains come first; configured-only chains are appended in the given order,
14
+ * de-duplicated.
10
15
  */
11
- function buildManagedAccount(signer, address, label, features) {
16
+ function chainsForNetworks(networks) {
17
+ const chains = [...SUI_CHAINS];
18
+ const seen = new Set(SUI_CHAINS);
19
+ for (const network of networks ?? []) {
20
+ const chain = `sui:${network}`;
21
+ if (!seen.has(chain)) {
22
+ seen.add(chain);
23
+ chains.push(chain);
24
+ }
25
+ }
26
+ return chains;
27
+ }
28
+ /**
29
+ * Build a {@link ManagedAccount} from a signer, address, label, optional
30
+ * feature list, and optional advertised chains. When `chains` is omitted the
31
+ * account advertises the standard {@link SUI_CHAINS}; pass the configured
32
+ * network names (via {@link chainsForNetworks}) so per-account advertised
33
+ * chains include fork/custom networks too.
34
+ */
35
+ function buildManagedAccount(signer, address, label, features, chains) {
12
36
  return {
13
37
  address,
14
38
  label,
@@ -17,12 +41,12 @@ function buildManagedAccount(signer, address, label, features) {
17
41
  address,
18
42
  label,
19
43
  publicKey: signer.getPublicKey().toSuiBytes(),
20
- chains: [...SUI_CHAINS],
44
+ chains: chains !== void 0 ? [...chains] : [...SUI_CHAINS],
21
45
  features: [...features ?? DEFAULT_FEATURES]
22
46
  })
23
47
  };
24
48
  }
25
49
  //#endregion
26
- export { buildManagedAccount };
50
+ export { buildManagedAccount, chainsForNetworks };
27
51
 
28
52
  //# sourceMappingURL=build-managed-account.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"build-managed-account.mjs","names":[],"sources":["../../src/adapters/build-managed-account.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { Signer } from '@mysten/sui/cryptography';\nimport type { IdentifierString } from '@mysten/wallet-standard';\nimport { ReadonlyWalletAccount, SUI_CHAINS } from '@mysten/wallet-standard';\n\nimport type { ManagedAccount } from '../types.js';\n\nconst DEFAULT_FEATURES: IdentifierString[] = [\n\t'sui:signTransaction',\n\t'sui:signAndExecuteTransaction',\n\t'sui:signPersonalMessage',\n];\n\n/**\n * Build a {@link ManagedAccount} from a signer, address, label, and optional feature list.\n */\nexport function buildManagedAccount(\n\tsigner: Signer,\n\taddress: string,\n\tlabel: string,\n\tfeatures?: readonly IdentifierString[],\n): ManagedAccount {\n\tconst walletAccount = new ReadonlyWalletAccount({\n\t\taddress,\n\t\tlabel,\n\t\tpublicKey: signer.getPublicKey().toSuiBytes(),\n\t\tchains: [...SUI_CHAINS],\n\t\tfeatures: [...(features ?? DEFAULT_FEATURES)],\n\t});\n\treturn { address, label, signer, walletAccount };\n}\n"],"mappings":";;AASA,MAAM,mBAAuC;CAC5C;CACA;CACA;AACD;;;;AAKA,SAAgB,oBACf,QACA,SACA,OACA,UACiB;CAQjB,OAAO;EAAE;EAAS;EAAO;EAAQ,mBAPP,sBAAsB;GAC/C;GACA;GACA,WAAW,OAAO,aAAa,CAAC,CAAC,WAAW;GAC5C,QAAQ,CAAC,GAAG,UAAU;GACtB,UAAU,CAAC,GAAI,YAAY,gBAAiB;EAC7C,CACiC;CAAc;AAChD"}
1
+ {"version":3,"file":"build-managed-account.mjs","names":[],"sources":["../../src/adapters/build-managed-account.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { Signer } from '@mysten/sui/cryptography';\nimport type { IdentifierString } from '@mysten/wallet-standard';\nimport { ReadonlyWalletAccount, SUI_CHAINS } from '@mysten/wallet-standard';\n\nimport type { ManagedAccount } from '../types.js';\n\nconst DEFAULT_FEATURES: IdentifierString[] = [\n\t'sui:signTransaction',\n\t'sui:signAndExecuteTransaction',\n\t'sui:signPersonalMessage',\n];\n\n/**\n * Resolve the wallet-standard chains to advertise for the given configured\n * network names. Each network is advertised as `sui:<name>` so dApp Kit's\n * chain-gated paths work for fork networks (`testnet-fork`, …) and custom\n * names, unioned with the standard {@link SUI_CHAINS} for safety. Standard\n * chains come first; configured-only chains are appended in the given order,\n * de-duplicated.\n */\nexport function chainsForNetworks(networks?: readonly string[]): `sui:${string}`[] {\n\tconst chains: `sui:${string}`[] = [...SUI_CHAINS];\n\tconst seen = new Set<string>(SUI_CHAINS);\n\tfor (const network of networks ?? []) {\n\t\tconst chain = `sui:${network}` as const;\n\t\tif (!seen.has(chain)) {\n\t\t\tseen.add(chain);\n\t\t\tchains.push(chain);\n\t\t}\n\t}\n\treturn chains;\n}\n\n/**\n * Build a {@link ManagedAccount} from a signer, address, label, optional\n * feature list, and optional advertised chains. When `chains` is omitted the\n * account advertises the standard {@link SUI_CHAINS}; pass the configured\n * network names (via {@link chainsForNetworks}) so per-account advertised\n * chains include fork/custom networks too.\n */\nexport function buildManagedAccount(\n\tsigner: Signer,\n\taddress: string,\n\tlabel: string,\n\tfeatures?: readonly IdentifierString[],\n\tchains?: readonly `sui:${string}`[],\n): ManagedAccount {\n\tconst walletAccount = new ReadonlyWalletAccount({\n\t\taddress,\n\t\tlabel,\n\t\tpublicKey: signer.getPublicKey().toSuiBytes(),\n\t\tchains: chains !== undefined ? [...chains] : [...SUI_CHAINS],\n\t\tfeatures: [...(features ?? DEFAULT_FEATURES)],\n\t});\n\treturn { address, label, signer, walletAccount };\n}\n"],"mappings":";;AASA,MAAM,mBAAuC;CAC5C;CACA;CACA;AACD;;;;;;;;;AAUA,SAAgB,kBAAkB,UAAiD;CAClF,MAAM,SAA4B,CAAC,GAAG,UAAU;CAChD,MAAM,OAAO,IAAI,IAAY,UAAU;CACvC,KAAK,MAAM,WAAW,YAAY,CAAC,GAAG;EACrC,MAAM,QAAQ,OAAO;EACrB,IAAI,CAAC,KAAK,IAAI,KAAK,GAAG;GACrB,KAAK,IAAI,KAAK;GACd,OAAO,KAAK,KAAK;EAClB;CACD;CACA,OAAO;AACR;;;;;;;;AASA,SAAgB,oBACf,QACA,SACA,OACA,UACA,QACiB;CAQjB,OAAO;EAAE;EAAS;EAAO;EAAQ,mBAPP,sBAAsB;GAC/C;GACA;GACA,WAAW,OAAO,aAAa,CAAC,CAAC,WAAW;GAC5C,QAAQ,WAAW,SAAY,CAAC,GAAG,MAAM,IAAI,CAAC,GAAG,UAAU;GAC3D,UAAU,CAAC,GAAI,YAAY,gBAAiB;EAC7C,CACiC;CAAc;AAChD"}
@@ -38,6 +38,11 @@ interface DevstackSignerAdapterOptions {
38
38
  token?: string | null;
39
39
  /** Override the adapter's display name. Defaults to `'Devstack'`. */
40
40
  name?: string;
41
+ /** The configured network names the wallet operates on. Each is advertised
42
+ * on every account as the wallet-standard chain `sui:<name>` (unioned with
43
+ * the standard Sui chains) so fork/custom networks are signable. Defaults
44
+ * to the standard chains only. */
45
+ networks?: readonly string[];
41
46
  }
42
47
  /**
43
48
  * {@link SignerAdapter} that surfaces every account resolved by `devstack up`
@@ -45,10 +50,9 @@ interface DevstackSignerAdapterOptions {
45
50
  * shipping their private keys into the frontend bundle. All signing happens
46
51
  * server-side via the `wallet()` plugin.
47
52
  *
48
- * The simplest construction uses devstack's generated `dapp-kit-config.ts`,
49
- * which bakes the wallet adapter wiring at codegen time — apps spread
50
- * `devstackDappKitConfig` into `createDAppKit(...)` and never touch the
51
- * adapter directly.
53
+ * The dev wallet that devstack injects at serve time registers this adapter
54
+ * automatically from the wallet connection devstack publishes — apps wire
55
+ * dapp-kit in their own `dapp-kit.ts` and never touch the adapter directly.
52
56
  *
53
57
  * To construct one manually (e.g. from a non-manifest source):
54
58
  *
@@ -79,8 +83,8 @@ declare class DevstackSignerAdapter extends BaseSignerAdapter {
79
83
  */
80
84
  declare function parseDevstackToken(pairedUrl: string | undefined): string | null;
81
85
  /** Narrow input — the only field the adapter consumes is
82
- * `app.wallet.{url, pairUrl}`. Codegen emits exactly this shape so
83
- * generated `dapp-kit-config.ts` doesn't have to fabricate placeholder
86
+ * `app.wallet.{url, pairUrl}`. Codegen emits exactly this shape so the
87
+ * generated wiring doesn't have to fabricate placeholder
84
88
  * manifest fields (`stack.app`, `coins`, etc.) just to satisfy the
85
89
  * full `Manifest` type. Mirrors a slice of `AppManifest` from
86
90
  * `@mysten-incubation/devstack` without importing it (dev-wallet
@@ -104,8 +108,16 @@ interface DevstackAdapterManifest {
104
108
  * {@link DevstackAdapterManifest}, so
105
109
  * `createDevstackAdapterFromManifest(devstackManifest)` typechecks
106
110
  * without a cast.
111
+ *
112
+ * The narrow manifest shape carries no network set, so pass
113
+ * `options.networks` (e.g. the generated `config.networkNames`) to advertise
114
+ * fork/custom networks as `sui:<name>` on every account — matching what the
115
+ * devstack-injected dev wallet does via the constructor. Omit it and accounts
116
+ * advertise the standard Sui chains only.
107
117
  */
108
- declare function createDevstackAdapterFromManifest(manifest: DevstackAdapterManifest): DevstackSignerAdapter | null;
118
+ declare function createDevstackAdapterFromManifest(manifest: DevstackAdapterManifest, options?: {
119
+ readonly networks?: readonly string[];
120
+ }): DevstackSignerAdapter | null;
109
121
  //#endregion
110
122
  export { DevstackAdapterManifest, DevstackProxySigner, DevstackSignerAdapter, DevstackSignerAdapterOptions, createDevstackAdapterFromManifest, parseDevstackToken };
111
123
  //# sourceMappingURL=devstack-adapter.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"devstack-adapter.d.mts","names":[],"sources":["../../src/adapters/devstack-adapter.ts"],"mappings":";;;;;AAiEA;;;;cAAa,mBAAA,SAA4B,MAAA;EAAA;cAO5B,OAAA;IACX,OAAA;IACA,SAAA,EAAW,SAAA;IACX,MAAA,EAAQ,eAAA;IACR,YAAA;IACA,SAAA;EAAA;EAUK,IAAA,CAAK,MAAA,EAAQ,UAAA,GAAa,OAAA,CAAQ,UAAA,CAAW,WAAA;EAOnD,YAAA,IAAgB,eAAA;EAIhB,YAAA,IAAgB,SAAA;EAIP,YAAA;EAIM,eAAA,CAAgB,KAAA,EAAO,UAAA,GAAU,OAAA;;;;EAqCjC,mBAAA,CAAoB,KAAA,EAAO,UAAA,GAAU,OAAA;;;;;UAyCpC,4BAAA;EA3Gf;;EA8GD,YAAA;EApGmB;;EAuGnB,KAAA;EAvGwC;EAyGxC,IAAA;AAAA;;;;;;;;;;;;;;;;;;;AARD;;cA+Ba,qBAAA,SAA8B,iBAAA;EAAA;WACjC,EAAA;EAAA,SACA,IAAA;EAAA,SACA,aAAA;cAKG,OAAA,EAAS,4BAAA;EAYf,UAAA,IAAc,OAAA;EAaX,OAAA;AAAA;;;;;;;;;;iBAyCM,kBAAA,CAAmB,SAA6B;;;;;;;;UAsB/C,uBAAA;EAChB,GAAA;IACC,MAAA;MACC,GAAA;MACA,OAAA;IAAA;EAAA;AAAA;;;;;;;;;AAAO;AAiBV;;;iBAAgB,iCAAA,CACf,QAAA,EAAU,uBAAA,GACR,qBAAqB"}
1
+ {"version":3,"file":"devstack-adapter.d.mts","names":[],"sources":["../../src/adapters/devstack-adapter.ts"],"mappings":";;;;;AAiEA;;;;cAAa,mBAAA,SAA4B,MAAA;EAAA;cAO5B,OAAA;IACX,OAAA;IACA,SAAA,EAAW,SAAA;IACX,MAAA,EAAQ,eAAA;IACR,YAAA;IACA,SAAA;EAAA;EAUK,IAAA,CAAK,MAAA,EAAQ,UAAA,GAAa,OAAA,CAAQ,UAAA,CAAW,WAAA;EAOnD,YAAA,IAAgB,eAAA;EAIhB,YAAA,IAAgB,SAAA;EAIP,YAAA;EAIM,eAAA,CAAgB,KAAA,EAAO,UAAA,GAAU,OAAA;;;;EAqCjC,mBAAA,CAAoB,KAAA,EAAO,UAAA,GAAU,OAAA;;;;;UAyCpC,4BAAA;EA3Gf;;EA8GD,YAAA;EApGmB;;EAuGnB,KAAA;EAvGwC;EAyGxC,IAAA;EAlGA;;;;EAuGA,QAAA;AAAA;;;;;;;;;;;;;;AAbD;;;;;;cAmCa,qBAAA,SAA8B,iBAAA;EAAA;WACjC,EAAA;EAAA,SACA,IAAA;EAAA,SACA,aAAA;cAMG,OAAA,EAAS,4BAAA;EAaf,UAAA,IAAc,OAAA;EAaX,OAAA;AAAA;;;;;;;;;;iBA+CM,kBAAA,CAAmB,SAA6B;;;;;;AA/C/C;AA+CjB;UAsBiB,uBAAA;EAChB,GAAA;IACC,MAAA;MACC,GAAA;MACA,OAAA;IAAA;EAAA;AAAA;;;;;;AAAO;AAuBV;;;;;;;;;;AAGwB;;iBAHR,iCAAA,CACf,QAAA,EAAU,uBAAA,EACV,OAAA;EAAA,SAAqB,QAAA;AAAA,IACnB,qBAAqB"}
@@ -1,4 +1,4 @@
1
- import { buildManagedAccount } from "./build-managed-account.mjs";
1
+ import { buildManagedAccount, chainsForNetworks } from "./build-managed-account.mjs";
2
2
  import { BaseSignerAdapter } from "./base-adapter.mjs";
3
3
  import { DEVSTACK_WALLET_HTTP_PATH } from "./devstack-paths.mjs";
4
4
  import { fromBase64, toBase64 } from "@mysten/sui/utils";
@@ -112,10 +112,9 @@ var DevstackProxySigner = class extends Signer {
112
112
  * shipping their private keys into the frontend bundle. All signing happens
113
113
  * server-side via the `wallet()` plugin.
114
114
  *
115
- * The simplest construction uses devstack's generated `dapp-kit-config.ts`,
116
- * which bakes the wallet adapter wiring at codegen time — apps spread
117
- * `devstackDappKitConfig` into `createDAppKit(...)` and never touch the
118
- * adapter directly.
115
+ * The dev wallet that devstack injects at serve time registers this adapter
116
+ * automatically from the wallet connection devstack publishes — apps wire
117
+ * dapp-kit in their own `dapp-kit.ts` and never touch the adapter directly.
119
118
  *
120
119
  * To construct one manually (e.g. from a non-manifest source):
121
120
  *
@@ -129,6 +128,7 @@ var DevstackProxySigner = class extends Signer {
129
128
  var DevstackSignerAdapter = class extends BaseSignerAdapter {
130
129
  #serverOrigin;
131
130
  #authToken;
131
+ #chains;
132
132
  constructor(options) {
133
133
  super();
134
134
  this.id = "devstack";
@@ -141,6 +141,7 @@ var DevstackSignerAdapter = class extends BaseSignerAdapter {
141
141
  this.#serverOrigin = options.serverOrigin;
142
142
  this.#authToken = options.token ?? null;
143
143
  this.name = options.name ?? "Devstack";
144
+ this.#chains = chainsForNetworks(options.networks);
144
145
  }
145
146
  async initialize() {
146
147
  try {
@@ -173,7 +174,7 @@ var DevstackSignerAdapter = class extends BaseSignerAdapter {
173
174
  scheme: info.scheme,
174
175
  serverOrigin: this.#serverOrigin,
175
176
  authToken: this.#authToken
176
- }), info.address, info.name, DEVSTACK_WALLET_FEATURES);
177
+ }), info.address, info.name, DEVSTACK_WALLET_FEATURES, this.#chains);
177
178
  });
178
179
  }
179
180
  };
@@ -209,13 +210,20 @@ function parseDevstackToken(pairedUrl) {
209
210
  * {@link DevstackAdapterManifest}, so
210
211
  * `createDevstackAdapterFromManifest(devstackManifest)` typechecks
211
212
  * without a cast.
213
+ *
214
+ * The narrow manifest shape carries no network set, so pass
215
+ * `options.networks` (e.g. the generated `config.networkNames`) to advertise
216
+ * fork/custom networks as `sui:<name>` on every account — matching what the
217
+ * devstack-injected dev wallet does via the constructor. Omit it and accounts
218
+ * advertise the standard Sui chains only.
212
219
  */
213
- function createDevstackAdapterFromManifest(manifest) {
220
+ function createDevstackAdapterFromManifest(manifest, options) {
214
221
  const wallet = manifest.app?.wallet;
215
222
  if (wallet === void 0) return null;
216
223
  return new DevstackSignerAdapter({
217
224
  serverOrigin: wallet.url,
218
- token: parseDevstackToken(wallet.pairUrl)
225
+ token: parseDevstackToken(wallet.pairUrl),
226
+ networks: options?.networks
219
227
  });
220
228
  }
221
229
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"devstack-adapter.mjs","names":["#address","#publicKey","#scheme","#serverOrigin","#authToken","#fetchAccounts"],"sources":["../../src/adapters/devstack-adapter.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n//\n// Devstack signer adapter — exposes accounts resolved by `devstack up`'s\n// `wallet()` plugin to dApp Kit, signing transactions over HTTP so\n// private keys never enter the frontend bundle. Mirrors RemoteCliAdapter's\n// out-of-process model; the difference is the source of accounts (devstack\n// resolved signers vs `sui keytool list`) and the endpoint paths.\n\nimport { Signer } from '@mysten/sui/cryptography';\nimport type { PublicKey, SignatureScheme } from '@mysten/sui/cryptography';\nimport { Ed25519PublicKey } from '@mysten/sui/keypairs/ed25519';\nimport { Secp256k1PublicKey } from '@mysten/sui/keypairs/secp256k1';\nimport { Secp256r1PublicKey } from '@mysten/sui/keypairs/secp256r1';\nimport { fromBase64, toBase64 } from '@mysten/sui/utils';\n\nimport type { ManagedAccount } from '../types.js';\nimport { BaseSignerAdapter } from './base-adapter.js';\nimport { buildManagedAccount } from './build-managed-account.js';\nimport { DEVSTACK_WALLET_HTTP_PATH } from './devstack-paths.js';\n\nconst DEVSTACK_WALLET_FEATURES = [\n\t'sui:signTransaction',\n\t'sui:signAndExecuteTransaction',\n\t'sui:signPersonalMessage',\n] as const;\n\ninterface ServerAccountInfo {\n\tname: string;\n\taddress: string;\n\tscheme: SignatureScheme;\n\tpublicKey: string;\n}\n\nclass AuthError extends Error {\n\tconstructor() {\n\t\tsuper('Unauthorized');\n\t\tthis.name = 'AuthError';\n\t}\n}\n\nfunction publicKeyForScheme(base64: string, scheme: SignatureScheme | string): PublicKey {\n\tconst bytes = fromBase64(base64);\n\t// The devstack server's wallet-app endpoint surfaces the scheme in\n\t// the lowercase form it standardised at the Account boundary\n\t// (`'ed25519'`, `'secp256k1'`, `'secp256r1'`). The Sui SDK's\n\t// `SignatureScheme` union is mixed-case (`'ED25519'`, `'Secp256k1'`,\n\t// …). Normalise to lowercase before matching so either casing parses.\n\tswitch (String(scheme).toLowerCase()) {\n\t\tcase 'ed25519':\n\t\t\treturn new Ed25519PublicKey(bytes);\n\t\tcase 'secp256k1':\n\t\t\treturn new Secp256k1PublicKey(bytes);\n\t\tcase 'secp256r1':\n\t\t\treturn new Secp256r1PublicKey(bytes);\n\t\tdefault:\n\t\t\tthrow new Error(`Unsupported key scheme: ${scheme}`);\n\t}\n}\n\n/**\n * Signer that delegates transaction signing to the devstack wallet-app\n * over HTTP. Mirrors {@link CliProxySigner} but talks to the\n * `wallet()` plugin's endpoints under `/api/v1/devstack/*`.\n */\nexport class DevstackProxySigner extends Signer {\n\t#address: string;\n\t#publicKey: PublicKey;\n\t#scheme: SignatureScheme;\n\t#serverOrigin: string;\n\t#authToken: string | null;\n\n\tconstructor(options: {\n\t\taddress: string;\n\t\tpublicKey: PublicKey;\n\t\tscheme: SignatureScheme;\n\t\tserverOrigin: string;\n\t\tauthToken?: string | null;\n\t}) {\n\t\tsuper();\n\t\tthis.#address = options.address;\n\t\tthis.#publicKey = options.publicKey;\n\t\tthis.#scheme = options.scheme;\n\t\tthis.#serverOrigin = options.serverOrigin;\n\t\tthis.#authToken = options.authToken ?? null;\n\t}\n\n\tasync sign(_bytes: Uint8Array): Promise<Uint8Array<ArrayBuffer>> {\n\t\tthrow new Error(\n\t\t\t'DevstackProxySigner does not support raw digest signing. ' +\n\t\t\t\t'Use signTransaction (the wallet-app signs BCS-serialized TransactionData).',\n\t\t);\n\t}\n\n\tgetKeyScheme(): SignatureScheme {\n\t\treturn this.#scheme;\n\t}\n\n\tgetPublicKey(): PublicKey {\n\t\treturn this.#publicKey;\n\t}\n\n\toverride toSuiAddress(): string {\n\t\treturn this.#address;\n\t}\n\n\toverride async signTransaction(bytes: Uint8Array) {\n\t\tconst headers: Record<string, string> = { 'Content-Type': 'application/json' };\n\t\tif (this.#authToken !== null) {\n\t\t\theaders['Authorization'] = `Bearer ${this.#authToken}`;\n\t\t}\n\t\tconst requestBytes = toBase64(bytes);\n\t\tconst res = await fetch(`${this.#serverOrigin}${DEVSTACK_WALLET_HTTP_PATH.SIGN_TRANSACTION}`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders,\n\t\t\tbody: JSON.stringify({\n\t\t\t\taddress: this.#address,\n\t\t\t\tbytes: requestBytes,\n\t\t\t}),\n\t\t});\n\t\tif (!res.ok) {\n\t\t\tconst body = await res.json().catch(() => ({}));\n\t\t\tconst message = (body as { error?: unknown }).error;\n\t\t\tthrow new Error(\n\t\t\t\t`Devstack signing failed: ${typeof message === 'string' ? message : res.statusText}`,\n\t\t\t);\n\t\t}\n\t\tconst { bytes: responseBytes, signature } = (await res.json()) as {\n\t\t\tbytes?: string;\n\t\t\tsignature?: string;\n\t\t};\n\t\tif (typeof responseBytes !== 'string' || responseBytes.length === 0) {\n\t\t\tthrow new Error('Devstack signing failed: server returned invalid signed bytes');\n\t\t}\n\t\tif (typeof signature !== 'string' || signature.length === 0) {\n\t\t\tthrow new Error('Devstack signing failed: server returned invalid signature');\n\t\t}\n\t\treturn {\n\t\t\tbytes: responseBytes,\n\t\t\tsignature,\n\t\t};\n\t}\n\n\toverride async signPersonalMessage(bytes: Uint8Array) {\n\t\tconst headers: Record<string, string> = { 'Content-Type': 'application/json' };\n\t\tif (this.#authToken !== null) {\n\t\t\theaders['Authorization'] = `Bearer ${this.#authToken}`;\n\t\t}\n\t\tconst res = await fetch(\n\t\t\t`${this.#serverOrigin}${DEVSTACK_WALLET_HTTP_PATH.SIGN_PERSONAL_MESSAGE}`,\n\t\t\t{\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders,\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\taddress: this.#address,\n\t\t\t\t\tbytes: toBase64(bytes),\n\t\t\t\t}),\n\t\t\t},\n\t\t);\n\t\tif (!res.ok) {\n\t\t\tconst body = await res.json().catch(() => ({}));\n\t\t\tconst message = (body as { error?: unknown }).error;\n\t\t\tthrow new Error(\n\t\t\t\t`Devstack personal-message signing failed: ${\n\t\t\t\t\ttypeof message === 'string' ? message : res.statusText\n\t\t\t\t}`,\n\t\t\t);\n\t\t}\n\t\tconst { bytes: responseBytes, signature } = (await res.json()) as {\n\t\t\tbytes?: string;\n\t\t\tsignature?: string;\n\t\t};\n\t\tif (typeof responseBytes !== 'string' || responseBytes.length === 0) {\n\t\t\tthrow new Error(\n\t\t\t\t'Devstack personal-message signing failed: server returned invalid signed bytes',\n\t\t\t);\n\t\t}\n\t\tif (typeof signature !== 'string' || signature.length === 0) {\n\t\t\tthrow new Error('Devstack personal-message signing failed: server returned no signature');\n\t\t}\n\t\treturn { bytes: responseBytes, signature };\n\t}\n}\n\nexport interface DevstackSignerAdapterOptions {\n\t/** Wallet-app origin (e.g. `http://localhost:9420`). Read from the\n\t * matching endpoint entry on the active manifest. */\n\tserverOrigin: string;\n\t/** Bearer token. Parse `?token=<hex>` from the manifest entry's\n\t * `pairUrl`. Omit if the wallet-app runs without auth. */\n\ttoken?: string | null;\n\t/** Override the adapter's display name. Defaults to `'Devstack'`. */\n\tname?: string;\n}\n\n/**\n * {@link SignerAdapter} that surfaces every account resolved by `devstack up`\n * (including `cliSigner`/`envSigner`/`generatedKeypair` slots) without\n * shipping their private keys into the frontend bundle. All signing happens\n * server-side via the `wallet()` plugin.\n *\n * The simplest construction uses devstack's generated `dapp-kit-config.ts`,\n * which bakes the wallet adapter wiring at codegen time — apps spread\n * `devstackDappKitConfig` into `createDAppKit(...)` and never touch the\n * adapter directly.\n *\n * To construct one manually (e.g. from a non-manifest source):\n *\n * ```ts\n * const adapter = new DevstackSignerAdapter({\n * serverOrigin: 'http://localhost:9420',\n * token: '<bearer hex>',\n * });\n * ```\n */\nexport class DevstackSignerAdapter extends BaseSignerAdapter {\n\treadonly id = 'devstack';\n\treadonly name: string;\n\treadonly allowAutoSign = true;\n\n\t#serverOrigin: string;\n\t#authToken: string | null;\n\n\tconstructor(options: DevstackSignerAdapterOptions) {\n\t\tsuper();\n\t\ttry {\n\t\t\tnew URL(options.serverOrigin);\n\t\t} catch {\n\t\t\tthrow new Error(`DevstackSignerAdapter: invalid serverOrigin \"${options.serverOrigin}\"`);\n\t\t}\n\t\tthis.#serverOrigin = options.serverOrigin;\n\t\tthis.#authToken = options.token ?? null;\n\t\tthis.name = options.name ?? 'Devstack';\n\t}\n\n\tasync initialize(): Promise<void> {\n\t\ttry {\n\t\t\tconst accounts = await this.#fetchAccounts();\n\t\t\tthis.setInitialAccounts(accounts);\n\t\t} catch (error) {\n\t\t\tif (error instanceof AuthError) {\n\t\t\t\tconsole.warn('[dev-wallet] devstack adapter: unauthorized — skipping account import');\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconsole.warn('[dev-wallet] devstack adapter: initialization failed:', error);\n\t\t}\n\t}\n\n\toverride destroy(): void {\n\t\tthis.#authToken = null;\n\t\tsuper.destroy();\n\t}\n\n\tasync #fetchAccounts(): Promise<ManagedAccount[]> {\n\t\tconst headers: Record<string, string> = { 'Content-Type': 'application/json' };\n\t\tif (this.#authToken !== null) {\n\t\t\theaders['Authorization'] = `Bearer ${this.#authToken}`;\n\t\t}\n\t\tconst res = await fetch(`${this.#serverOrigin}${DEVSTACK_WALLET_HTTP_PATH.ACCOUNTS}`, {\n\t\t\theaders,\n\t\t});\n\t\tif (res.status === 401 || res.status === 403) throw new AuthError();\n\t\tif (!res.ok) {\n\t\t\tthrow new Error(`Failed to fetch devstack accounts: ${res.statusText}`);\n\t\t}\n\t\tconst { accounts } = (await res.json()) as { accounts: ServerAccountInfo[] };\n\t\treturn accounts.map((info) => {\n\t\t\tconst publicKey = publicKeyForScheme(info.publicKey, info.scheme);\n\t\t\tconst signer = new DevstackProxySigner({\n\t\t\t\taddress: info.address,\n\t\t\t\tpublicKey,\n\t\t\t\tscheme: info.scheme,\n\t\t\t\tserverOrigin: this.#serverOrigin,\n\t\t\t\tauthToken: this.#authToken,\n\t\t\t});\n\t\t\treturn buildManagedAccount(signer, info.address, info.name, DEVSTACK_WALLET_FEATURES);\n\t\t});\n\t}\n}\n\n/**\n * Pull the bearer token off a paired URL produced by the `wallet()`\n * plugin. Returns `null` if the input is undefined, malformed, or\n * carries a `<redacted>` placeholder (the manifest's pairUrl carries\n * the redacted form; the real token lives in a sibling 0o600 file the\n * consumer reads separately).\n *\n * The token rides in the URL fragment as `#token=<hex>`.\n */\nexport function parseDevstackToken(pairedUrl: string | undefined): string | null {\n\tif (pairedUrl === undefined) return null;\n\ttry {\n\t\tconst url = new URL(pairedUrl);\n\t\tconst hash = url.hash; // e.g. \"#token=abcd\"\n\t\tif (hash.length <= 1) return null;\n\t\tconst params = new URLSearchParams(hash.slice(1));\n\t\tconst token = params.get('token');\n\t\tif (token === null || token === '<redacted>') return null;\n\t\treturn token;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/** Narrow input — the only field the adapter consumes is\n * `app.wallet.{url, pairUrl}`. Codegen emits exactly this shape so\n * generated `dapp-kit-config.ts` doesn't have to fabricate placeholder\n * manifest fields (`stack.app`, `coins`, etc.) just to satisfy the\n * full `Manifest` type. Mirrors a slice of `AppManifest` from\n * `@mysten-incubation/devstack` without importing it (dev-wallet\n * doesn't depend on devstack — circular). */\nexport interface DevstackAdapterManifest {\n\tapp?: {\n\t\twallet?: {\n\t\t\turl: string;\n\t\t\tpairUrl?: string;\n\t\t};\n\t};\n}\n\n/**\n * Convenience: read the wallet-app endpoint off a devstack manifest and\n * build a configured adapter, or return `null` when the entry isn't\n * present (no `wallet(...)` in the stack, or it hasn't come up yet).\n *\n * The wallet entry's `pairUrl` carries the `#token=…` fragment used to\n * extract the bearer token. The full `Manifest` shape from\n * `@mysten-incubation/devstack` is structurally compatible with\n * {@link DevstackAdapterManifest}, so\n * `createDevstackAdapterFromManifest(devstackManifest)` typechecks\n * without a cast.\n */\nexport function createDevstackAdapterFromManifest(\n\tmanifest: DevstackAdapterManifest,\n): DevstackSignerAdapter | null {\n\tconst wallet = manifest.app?.wallet;\n\tif (wallet === undefined) return null;\n\treturn new DevstackSignerAdapter({\n\t\tserverOrigin: wallet.url,\n\t\ttoken: parseDevstackToken(wallet.pairUrl),\n\t});\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"devstack-adapter.mjs","names":["#address","#publicKey","#scheme","#serverOrigin","#authToken","#chains","#fetchAccounts"],"sources":["../../src/adapters/devstack-adapter.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n//\n// Devstack signer adapter — exposes accounts resolved by `devstack up`'s\n// `wallet()` plugin to dApp Kit, signing transactions over HTTP so\n// private keys never enter the frontend bundle. Mirrors RemoteCliAdapter's\n// out-of-process model; the difference is the source of accounts (devstack\n// resolved signers vs `sui keytool list`) and the endpoint paths.\n\nimport { Signer } from '@mysten/sui/cryptography';\nimport type { PublicKey, SignatureScheme } from '@mysten/sui/cryptography';\nimport { Ed25519PublicKey } from '@mysten/sui/keypairs/ed25519';\nimport { Secp256k1PublicKey } from '@mysten/sui/keypairs/secp256k1';\nimport { Secp256r1PublicKey } from '@mysten/sui/keypairs/secp256r1';\nimport { fromBase64, toBase64 } from '@mysten/sui/utils';\n\nimport type { ManagedAccount } from '../types.js';\nimport { BaseSignerAdapter } from './base-adapter.js';\nimport { buildManagedAccount, chainsForNetworks } from './build-managed-account.js';\nimport { DEVSTACK_WALLET_HTTP_PATH } from './devstack-paths.js';\n\nconst DEVSTACK_WALLET_FEATURES = [\n\t'sui:signTransaction',\n\t'sui:signAndExecuteTransaction',\n\t'sui:signPersonalMessage',\n] as const;\n\ninterface ServerAccountInfo {\n\tname: string;\n\taddress: string;\n\tscheme: SignatureScheme;\n\tpublicKey: string;\n}\n\nclass AuthError extends Error {\n\tconstructor() {\n\t\tsuper('Unauthorized');\n\t\tthis.name = 'AuthError';\n\t}\n}\n\nfunction publicKeyForScheme(base64: string, scheme: SignatureScheme | string): PublicKey {\n\tconst bytes = fromBase64(base64);\n\t// The devstack server's wallet-app endpoint surfaces the scheme in\n\t// the lowercase form it standardised at the Account boundary\n\t// (`'ed25519'`, `'secp256k1'`, `'secp256r1'`). The Sui SDK's\n\t// `SignatureScheme` union is mixed-case (`'ED25519'`, `'Secp256k1'`,\n\t// …). Normalise to lowercase before matching so either casing parses.\n\tswitch (String(scheme).toLowerCase()) {\n\t\tcase 'ed25519':\n\t\t\treturn new Ed25519PublicKey(bytes);\n\t\tcase 'secp256k1':\n\t\t\treturn new Secp256k1PublicKey(bytes);\n\t\tcase 'secp256r1':\n\t\t\treturn new Secp256r1PublicKey(bytes);\n\t\tdefault:\n\t\t\tthrow new Error(`Unsupported key scheme: ${scheme}`);\n\t}\n}\n\n/**\n * Signer that delegates transaction signing to the devstack wallet-app\n * over HTTP. Mirrors {@link CliProxySigner} but talks to the\n * `wallet()` plugin's endpoints under `/api/v1/devstack/*`.\n */\nexport class DevstackProxySigner extends Signer {\n\t#address: string;\n\t#publicKey: PublicKey;\n\t#scheme: SignatureScheme;\n\t#serverOrigin: string;\n\t#authToken: string | null;\n\n\tconstructor(options: {\n\t\taddress: string;\n\t\tpublicKey: PublicKey;\n\t\tscheme: SignatureScheme;\n\t\tserverOrigin: string;\n\t\tauthToken?: string | null;\n\t}) {\n\t\tsuper();\n\t\tthis.#address = options.address;\n\t\tthis.#publicKey = options.publicKey;\n\t\tthis.#scheme = options.scheme;\n\t\tthis.#serverOrigin = options.serverOrigin;\n\t\tthis.#authToken = options.authToken ?? null;\n\t}\n\n\tasync sign(_bytes: Uint8Array): Promise<Uint8Array<ArrayBuffer>> {\n\t\tthrow new Error(\n\t\t\t'DevstackProxySigner does not support raw digest signing. ' +\n\t\t\t\t'Use signTransaction (the wallet-app signs BCS-serialized TransactionData).',\n\t\t);\n\t}\n\n\tgetKeyScheme(): SignatureScheme {\n\t\treturn this.#scheme;\n\t}\n\n\tgetPublicKey(): PublicKey {\n\t\treturn this.#publicKey;\n\t}\n\n\toverride toSuiAddress(): string {\n\t\treturn this.#address;\n\t}\n\n\toverride async signTransaction(bytes: Uint8Array) {\n\t\tconst headers: Record<string, string> = { 'Content-Type': 'application/json' };\n\t\tif (this.#authToken !== null) {\n\t\t\theaders['Authorization'] = `Bearer ${this.#authToken}`;\n\t\t}\n\t\tconst requestBytes = toBase64(bytes);\n\t\tconst res = await fetch(`${this.#serverOrigin}${DEVSTACK_WALLET_HTTP_PATH.SIGN_TRANSACTION}`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders,\n\t\t\tbody: JSON.stringify({\n\t\t\t\taddress: this.#address,\n\t\t\t\tbytes: requestBytes,\n\t\t\t}),\n\t\t});\n\t\tif (!res.ok) {\n\t\t\tconst body = await res.json().catch(() => ({}));\n\t\t\tconst message = (body as { error?: unknown }).error;\n\t\t\tthrow new Error(\n\t\t\t\t`Devstack signing failed: ${typeof message === 'string' ? message : res.statusText}`,\n\t\t\t);\n\t\t}\n\t\tconst { bytes: responseBytes, signature } = (await res.json()) as {\n\t\t\tbytes?: string;\n\t\t\tsignature?: string;\n\t\t};\n\t\tif (typeof responseBytes !== 'string' || responseBytes.length === 0) {\n\t\t\tthrow new Error('Devstack signing failed: server returned invalid signed bytes');\n\t\t}\n\t\tif (typeof signature !== 'string' || signature.length === 0) {\n\t\t\tthrow new Error('Devstack signing failed: server returned invalid signature');\n\t\t}\n\t\treturn {\n\t\t\tbytes: responseBytes,\n\t\t\tsignature,\n\t\t};\n\t}\n\n\toverride async signPersonalMessage(bytes: Uint8Array) {\n\t\tconst headers: Record<string, string> = { 'Content-Type': 'application/json' };\n\t\tif (this.#authToken !== null) {\n\t\t\theaders['Authorization'] = `Bearer ${this.#authToken}`;\n\t\t}\n\t\tconst res = await fetch(\n\t\t\t`${this.#serverOrigin}${DEVSTACK_WALLET_HTTP_PATH.SIGN_PERSONAL_MESSAGE}`,\n\t\t\t{\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders,\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\taddress: this.#address,\n\t\t\t\t\tbytes: toBase64(bytes),\n\t\t\t\t}),\n\t\t\t},\n\t\t);\n\t\tif (!res.ok) {\n\t\t\tconst body = await res.json().catch(() => ({}));\n\t\t\tconst message = (body as { error?: unknown }).error;\n\t\t\tthrow new Error(\n\t\t\t\t`Devstack personal-message signing failed: ${\n\t\t\t\t\ttypeof message === 'string' ? message : res.statusText\n\t\t\t\t}`,\n\t\t\t);\n\t\t}\n\t\tconst { bytes: responseBytes, signature } = (await res.json()) as {\n\t\t\tbytes?: string;\n\t\t\tsignature?: string;\n\t\t};\n\t\tif (typeof responseBytes !== 'string' || responseBytes.length === 0) {\n\t\t\tthrow new Error(\n\t\t\t\t'Devstack personal-message signing failed: server returned invalid signed bytes',\n\t\t\t);\n\t\t}\n\t\tif (typeof signature !== 'string' || signature.length === 0) {\n\t\t\tthrow new Error('Devstack personal-message signing failed: server returned no signature');\n\t\t}\n\t\treturn { bytes: responseBytes, signature };\n\t}\n}\n\nexport interface DevstackSignerAdapterOptions {\n\t/** Wallet-app origin (e.g. `http://localhost:9420`). Read from the\n\t * matching endpoint entry on the active manifest. */\n\tserverOrigin: string;\n\t/** Bearer token. Parse `?token=<hex>` from the manifest entry's\n\t * `pairUrl`. Omit if the wallet-app runs without auth. */\n\ttoken?: string | null;\n\t/** Override the adapter's display name. Defaults to `'Devstack'`. */\n\tname?: string;\n\t/** The configured network names the wallet operates on. Each is advertised\n\t * on every account as the wallet-standard chain `sui:<name>` (unioned with\n\t * the standard Sui chains) so fork/custom networks are signable. Defaults\n\t * to the standard chains only. */\n\tnetworks?: readonly string[];\n}\n\n/**\n * {@link SignerAdapter} that surfaces every account resolved by `devstack up`\n * (including `cliSigner`/`envSigner`/`generatedKeypair` slots) without\n * shipping their private keys into the frontend bundle. All signing happens\n * server-side via the `wallet()` plugin.\n *\n * The dev wallet that devstack injects at serve time registers this adapter\n * automatically from the wallet connection devstack publishes — apps wire\n * dapp-kit in their own `dapp-kit.ts` and never touch the adapter directly.\n *\n * To construct one manually (e.g. from a non-manifest source):\n *\n * ```ts\n * const adapter = new DevstackSignerAdapter({\n * serverOrigin: 'http://localhost:9420',\n * token: '<bearer hex>',\n * });\n * ```\n */\nexport class DevstackSignerAdapter extends BaseSignerAdapter {\n\treadonly id = 'devstack';\n\treadonly name: string;\n\treadonly allowAutoSign = true;\n\n\t#serverOrigin: string;\n\t#authToken: string | null;\n\t#chains: readonly `sui:${string}`[];\n\n\tconstructor(options: DevstackSignerAdapterOptions) {\n\t\tsuper();\n\t\ttry {\n\t\t\tnew URL(options.serverOrigin);\n\t\t} catch {\n\t\t\tthrow new Error(`DevstackSignerAdapter: invalid serverOrigin \"${options.serverOrigin}\"`);\n\t\t}\n\t\tthis.#serverOrigin = options.serverOrigin;\n\t\tthis.#authToken = options.token ?? null;\n\t\tthis.name = options.name ?? 'Devstack';\n\t\tthis.#chains = chainsForNetworks(options.networks);\n\t}\n\n\tasync initialize(): Promise<void> {\n\t\ttry {\n\t\t\tconst accounts = await this.#fetchAccounts();\n\t\t\tthis.setInitialAccounts(accounts);\n\t\t} catch (error) {\n\t\t\tif (error instanceof AuthError) {\n\t\t\t\tconsole.warn('[dev-wallet] devstack adapter: unauthorized — skipping account import');\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconsole.warn('[dev-wallet] devstack adapter: initialization failed:', error);\n\t\t}\n\t}\n\n\toverride destroy(): void {\n\t\tthis.#authToken = null;\n\t\tsuper.destroy();\n\t}\n\n\tasync #fetchAccounts(): Promise<ManagedAccount[]> {\n\t\tconst headers: Record<string, string> = { 'Content-Type': 'application/json' };\n\t\tif (this.#authToken !== null) {\n\t\t\theaders['Authorization'] = `Bearer ${this.#authToken}`;\n\t\t}\n\t\tconst res = await fetch(`${this.#serverOrigin}${DEVSTACK_WALLET_HTTP_PATH.ACCOUNTS}`, {\n\t\t\theaders,\n\t\t});\n\t\tif (res.status === 401 || res.status === 403) throw new AuthError();\n\t\tif (!res.ok) {\n\t\t\tthrow new Error(`Failed to fetch devstack accounts: ${res.statusText}`);\n\t\t}\n\t\tconst { accounts } = (await res.json()) as { accounts: ServerAccountInfo[] };\n\t\treturn accounts.map((info) => {\n\t\t\tconst publicKey = publicKeyForScheme(info.publicKey, info.scheme);\n\t\t\tconst signer = new DevstackProxySigner({\n\t\t\t\taddress: info.address,\n\t\t\t\tpublicKey,\n\t\t\t\tscheme: info.scheme,\n\t\t\t\tserverOrigin: this.#serverOrigin,\n\t\t\t\tauthToken: this.#authToken,\n\t\t\t});\n\t\t\treturn buildManagedAccount(\n\t\t\t\tsigner,\n\t\t\t\tinfo.address,\n\t\t\t\tinfo.name,\n\t\t\t\tDEVSTACK_WALLET_FEATURES,\n\t\t\t\tthis.#chains,\n\t\t\t);\n\t\t});\n\t}\n}\n\n/**\n * Pull the bearer token off a paired URL produced by the `wallet()`\n * plugin. Returns `null` if the input is undefined, malformed, or\n * carries a `<redacted>` placeholder (the manifest's pairUrl carries\n * the redacted form; the real token lives in a sibling 0o600 file the\n * consumer reads separately).\n *\n * The token rides in the URL fragment as `#token=<hex>`.\n */\nexport function parseDevstackToken(pairedUrl: string | undefined): string | null {\n\tif (pairedUrl === undefined) return null;\n\ttry {\n\t\tconst url = new URL(pairedUrl);\n\t\tconst hash = url.hash; // e.g. \"#token=abcd\"\n\t\tif (hash.length <= 1) return null;\n\t\tconst params = new URLSearchParams(hash.slice(1));\n\t\tconst token = params.get('token');\n\t\tif (token === null || token === '<redacted>') return null;\n\t\treturn token;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/** Narrow input — the only field the adapter consumes is\n * `app.wallet.{url, pairUrl}`. Codegen emits exactly this shape so the\n * generated wiring doesn't have to fabricate placeholder\n * manifest fields (`stack.app`, `coins`, etc.) just to satisfy the\n * full `Manifest` type. Mirrors a slice of `AppManifest` from\n * `@mysten-incubation/devstack` without importing it (dev-wallet\n * doesn't depend on devstack — circular). */\nexport interface DevstackAdapterManifest {\n\tapp?: {\n\t\twallet?: {\n\t\t\turl: string;\n\t\t\tpairUrl?: string;\n\t\t};\n\t};\n}\n\n/**\n * Convenience: read the wallet-app endpoint off a devstack manifest and\n * build a configured adapter, or return `null` when the entry isn't\n * present (no `wallet(...)` in the stack, or it hasn't come up yet).\n *\n * The wallet entry's `pairUrl` carries the `#token=…` fragment used to\n * extract the bearer token. The full `Manifest` shape from\n * `@mysten-incubation/devstack` is structurally compatible with\n * {@link DevstackAdapterManifest}, so\n * `createDevstackAdapterFromManifest(devstackManifest)` typechecks\n * without a cast.\n *\n * The narrow manifest shape carries no network set, so pass\n * `options.networks` (e.g. the generated `config.networkNames`) to advertise\n * fork/custom networks as `sui:<name>` on every account — matching what the\n * devstack-injected dev wallet does via the constructor. Omit it and accounts\n * advertise the standard Sui chains only.\n */\nexport function createDevstackAdapterFromManifest(\n\tmanifest: DevstackAdapterManifest,\n\toptions?: { readonly networks?: readonly string[] },\n): DevstackSignerAdapter | null {\n\tconst wallet = manifest.app?.wallet;\n\tif (wallet === undefined) return null;\n\treturn new DevstackSignerAdapter({\n\t\tserverOrigin: wallet.url,\n\t\ttoken: parseDevstackToken(wallet.pairUrl),\n\t\tnetworks: options?.networks,\n\t});\n}\n"],"mappings":"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"}
@@ -1,29 +1,50 @@
1
1
  import { AutoApprovePolicy, DevWallet } from "../wallet/dev-wallet.mjs";
2
2
 
3
3
  //#region src/inject/index.d.ts
4
- /** Shape of a single entry in the generated `accounts` map
5
- * (`generated-extras/accounts.ts`). Only `address` is consumed here. */
4
+ /** Shape of a single entry in the dev `accounts` map (resolved off the
5
+ * injected deployment envelope via `resolveAccounts()`). Only `address` is
6
+ * consumed here. */
6
7
  interface DevstackAccountInfo {
7
8
  readonly address: string;
8
9
  readonly name?: string;
9
10
  }
11
+ /** One network the dev wallet operates on — its routed RPC endpoint plus an
12
+ * optional faucet. The wallet advertises every key as the wallet-standard
13
+ * chain `sui:<name>` and routes signing/execution to whichever network dApp
14
+ * Kit has selected; the active network's `faucet` drives a fund flow. The
15
+ * `rpc` MUST be the SAME routed RPC the app's dApp Kit client uses for that
16
+ * network (a raw `127.0.0.1:9000` is CORS-blocked from the routed page
17
+ * origin). The wallet is agnostic to live-vs-local — local and live networks
18
+ * are passed through the same map. */
19
+ interface DevstackNetworkInfo {
20
+ /** Routed RPC endpoint for this network. */
21
+ readonly rpc: string;
22
+ /** Optional faucet endpoint for this network (absent on live mainnet /
23
+ * fork stacks). */
24
+ readonly faucet?: string | null;
25
+ }
10
26
  interface RegisterDevstackDevWalletConfig {
11
- /** Wallet-app origin (`devWallet.walletUrl`). */
27
+ /** Wallet-app origin (the dev-wallet connection's `walletUrl`). */
12
28
  readonly serverOrigin: string;
13
- /** Bearer token (`parseDevstackToken(devWallet.pairUrl)`), or null. */
29
+ /** Bearer pairing token (read by the Vite dev server from the wallet's
30
+ * `0o600` side-channel token file), or null. */
14
31
  readonly token?: string | null;
15
- /** Generated name→account map (`accounts` from `generated-extras/accounts.ts`). */
32
+ /** Dev name→account map (resolved off the injected deployment envelope via
33
+ * `resolveAccounts()`). */
16
34
  readonly accounts: Readonly<Record<string, DevstackAccountInfo>>;
17
- /** RPC endpoint the wallet uses to execute `signAndExecuteTransaction`
18
- * (and simulate). MUST be the SAME routed RPC the app's dApp Kit client
19
- * uses (`config.networks[config.network].rpc`) a raw `127.0.0.1:9000`
20
- * is CORS-blocked from the routed page origin. When omitted the wallet
21
- * falls back to localnet defaults (only correct for non-routed setups). */
22
- readonly rpcUrl?: string;
23
- /** Network name the wallet's accounts are scoped to (e.g. `'localnet'`
24
- * from `devWallet.network`). The wallet advertises the wallet-standard
25
- * chain `sui:<network>` derived from it; defaults to `localnet`. */
26
- readonly network?: string;
35
+ /** The FULL network set the app supports `{ <name>: { rpc, faucet? } }`,
36
+ * covering BOTH the live local network and any live (devnet/testnet/…)
37
+ * networks the deployment envelope carries. The wallet advertises each as
38
+ * the wallet-standard chain `sui:<name>` and operates on whichever one
39
+ * dApp Kit has selected, so it persists across a UI `switchNetwork` (only
40
+ * the active chain changes — the wallet is registered once). When omitted
41
+ * the wallet falls back to a single localnet entry (the non-routed default
42
+ * only correct for a bare localnet setup). */
43
+ readonly networks?: Readonly<Record<string, DevstackNetworkInfo>>;
44
+ /** The network the wallet opens on (its initial active network). Must be a
45
+ * key of `networks`; defaults to the first `networks` key (`'localnet'`
46
+ * when `networks` is omitted). */
47
+ readonly defaultNetwork?: string;
27
48
  /** Auto-approve all signing requests (headless e2e). Defaults to false. */
28
49
  readonly autoApprove?: AutoApprovePolicy;
29
50
  /** Mount the floating wallet drawer UI. Defaults to true. */
@@ -57,5 +78,5 @@ declare global {
57
78
  */
58
79
  declare function registerDevstackDevWallet(config: RegisterDevstackDevWalletConfig): Promise<RegisterDevstackDevWalletResult>;
59
80
  //#endregion
60
- export { DevstackAccountInfo, RegisterDevstackDevWalletConfig, RegisterDevstackDevWalletResult, registerDevstackDevWallet };
81
+ export { DevstackAccountInfo, DevstackNetworkInfo, RegisterDevstackDevWalletConfig, RegisterDevstackDevWalletResult, registerDevstackDevWallet };
61
82
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/inject/index.ts"],"mappings":";;;;;UA6BiB,mBAAA;EAAA,SACP,OAAA;EAAA,SACA,IAAI;AAAA;AAAA,UAGG,+BAAA;EAAA;EAAA,SAEP,YAAA;;WAEA,KAAA;EAEmB;EAAA,SAAnB,QAAA,EAAU,QAAA,CAAS,MAAA,SAAe,mBAAA;EAYpB;;;;;EAAA,SANd,MAAA;EANU;;;EAAA,SAUV,OAAA;EAAA;EAAA,SAEA,WAAA,GAAc,iBAAA;EAAA;EAAA,SAEd,OAAA;EAEA;EAAA,SAAA,IAAA;AAAA;AAAA,UAGO,+BAAA;EAAA,SACP,MAAA,EAAQ,SAAS;;WAEjB,OAAA;AAAA;;QAIF,MAAA;EAAA,IAEH,uBAAA;EANY;AAChB;;;EADgB,IAYZ,4BAAA,EAA8B,OAAO,CAAC,+BAAA;AAAA;;;;;;;AAA4C;AAavF;;;iBAAgB,yBAAA,CACf,MAAA,EAAQ,+BAAA,GACN,OAAA,CAAQ,+BAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/inject/index.ts"],"mappings":";;;;;AA8BA;UAAiB,mBAAA;EAAA,SACP,OAAA;EAAA,SACA,IAAI;AAAA;AAWd;;;;AAKgB;AAGhB;;;AARA,UAAiB,mBAAA;EAgBY;EAAA,SAdnB,GAAA;EAuBmC;;EAAA,SApBnC,MAAM;AAAA;AAAA,UAGC,+BAAA;EAuBwB;EAAA,SArB/B,YAAA;EAGA;;EAAA,SAAA,KAAA;EAGmB;;EAAA,SAAnB,QAAA,EAAU,QAAA,CAAS,MAAA,SAAe,mBAAA;EASvB;;;;;;;;EAAA,SAAX,QAAA,GAAW,QAAA,CAAS,MAAA,SAAe,mBAAA;EAU/B;AAGd;;EAHc,SANJ,cAAA;EAUiB;EAAA,SARjB,WAAA,GAAc,iBAAA;EAQN;EAAA,SANR,OAAA;EAQO;EAAA,SANP,IAAA;AAAA;AAAA,UAGO,+BAAA;EAAA,SACP,MAAA,EAAQ,SAAS;EAce;EAAA,SAZhC,OAAA;AAAA;;QAIF,MAAA;EAAA,IAEH,uBAAA;EAMkF;;AAAA;AAavF;EAbuF,IAAlF,4BAAA,EAA8B,OAAO,CAAC,+BAAA;AAAA;;;;;;;;;;;iBAa3B,yBAAA,CACf,MAAA,EAAQ,+BAAA,GACN,OAAA,CAAQ,+BAAA"}
@@ -1,6 +1,5 @@
1
1
  import { mountAndRegisterDevWallet } from "../wallet/mount-and-register.mjs";
2
2
  import { DevstackSignerAdapter } from "../adapters/devstack-adapter.mjs";
3
- import { WebCryptoSignerAdapter } from "../adapters/webcrypto-adapter.mjs";
4
3
  //#region src/inject/index.ts
5
4
  /**
6
5
  * Construct + register the devstack dev wallet on the current page and wire
@@ -26,20 +25,35 @@ function registerDevstackDevWallet(config) {
26
25
  async function registerDevstackDevWalletImpl(config) {
27
26
  const { mountUI = true, autoApprove = false } = config;
28
27
  globalThis.__DEV_WALLET_INJECTED__ = true;
28
+ const networkInfos = config.networks !== void 0 && Object.keys(config.networks).length > 0 ? config.networks : { localnet: { rpc: "http://127.0.0.1:9000" } };
29
+ const networkNames = Object.keys(networkInfos);
29
30
  const adapter = new DevstackSignerAdapter({
30
31
  serverOrigin: config.serverOrigin,
31
32
  token: config.token ?? null,
32
- name: config.name ?? "Devstack"
33
+ name: config.name ?? "Devstack",
34
+ networks: networkNames
33
35
  });
34
- const rpcUrl = config.rpcUrl ?? "http://127.0.0.1:9000";
35
- const activeNetwork = config.network ?? "localnet";
36
- const networks = { [activeNetwork]: rpcUrl };
36
+ const networks = {};
37
+ const faucets = {};
38
+ for (const [net, info] of Object.entries(networkInfos)) {
39
+ networks[net] = info.rpc;
40
+ if (info.faucet !== void 0 && info.faucet !== null && info.faucet.length > 0) faucets[net] = info.faucet;
41
+ }
42
+ const activeNetwork = config.defaultNetwork !== void 0 && networks[config.defaultNetwork] !== void 0 ? config.defaultNetwork : Object.keys(networks)[0] ?? "localnet";
43
+ const adapters = [adapter];
44
+ try {
45
+ const { WebCryptoSignerAdapter } = await import("../adapters/webcrypto-adapter.mjs");
46
+ adapters.push(new WebCryptoSignerAdapter());
47
+ } catch (error) {
48
+ console.info("[dev-wallet] @mysten/signers is not installed — WebCrypto account creation is disabled. Install @mysten/signers to enable creating your own accounts in the dev wallet.", error);
49
+ }
37
50
  const { wallet, dispose: disposeWallet } = await mountAndRegisterDevWallet({
38
- adapters: [adapter, new WebCryptoSignerAdapter()],
51
+ adapters,
39
52
  name: config.name ?? "Devstack",
40
53
  autoApprove,
41
54
  autoConnect: Boolean(autoApprove),
42
55
  networks,
56
+ faucets,
43
57
  activeNetwork,
44
58
  mountUI
45
59
  });
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/inject/index.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n//\n// Page-register entry for the devstack dev wallet.\n//\n// This is the single function the devstack Vite plugin injects into a dev\n// page. It builds the token-based `DevstackSignerAdapter` (headless — signs\n// over HTTP, no popup) + the routed-RPC network map, then hands those to the\n// shared `mountAndRegisterDevWallet` core helper, which constructs the\n// `DevWallet`, registers it via wallet-standard (so dApp Kit auto-discovers\n// it), and mounts the floating wallet UI. The ONLY logic that lives here is\n// the devstack-integration glue: the adapter-from-config and the routed-RPC\n// network map.\n//\n// The wallet does NOT pre-connect or seed dApp Kit storage: a fresh page\n// loads disconnected, dApp Kit's own `autoConnect` only re-connects a genuine\n// prior session, and the Playwright `connectAs` helper performs an explicit\n// connection through dApp Kit's public API. That test bridge is registered\n// app-side via `@mysten-incubation/devstack/dapp-kit`'s\n// `registerDAppKitForTesting(dAppKit)` (DEV-only) — it needs the app's dApp\n// Kit instance, which the wallet has no reference to.\n\nimport type { AutoApprovePolicy, DevWallet } from '../wallet/dev-wallet.js';\nimport { mountAndRegisterDevWallet } from '../wallet/mount-and-register.js';\nimport { DevstackSignerAdapter } from '../adapters/devstack-adapter.js';\nimport { WebCryptoSignerAdapter } from '../adapters/webcrypto-adapter.js';\n\n/** Shape of a single entry in the generated `accounts` map\n * (`generated-extras/accounts.ts`). Only `address` is consumed here. */\nexport interface DevstackAccountInfo {\n\treadonly address: string;\n\treadonly name?: string;\n}\n\nexport interface RegisterDevstackDevWalletConfig {\n\t/** Wallet-app origin (`devWallet.walletUrl`). */\n\treadonly serverOrigin: string;\n\t/** Bearer token (`parseDevstackToken(devWallet.pairUrl)`), or null. */\n\treadonly token?: string | null;\n\t/** Generated name→account map (`accounts` from `generated-extras/accounts.ts`). */\n\treadonly accounts: Readonly<Record<string, DevstackAccountInfo>>;\n\t/** RPC endpoint the wallet uses to execute `signAndExecuteTransaction`\n\t * (and simulate). MUST be the SAME routed RPC the app's dApp Kit client\n\t * uses (`config.networks[config.network].rpc`) — a raw `127.0.0.1:9000`\n\t * is CORS-blocked from the routed page origin. When omitted the wallet\n\t * falls back to localnet defaults (only correct for non-routed setups). */\n\treadonly rpcUrl?: string;\n\t/** Network name the wallet's accounts are scoped to (e.g. `'localnet'`\n\t * from `devWallet.network`). The wallet advertises the wallet-standard\n\t * chain `sui:<network>` derived from it; defaults to `localnet`. */\n\treadonly network?: string;\n\t/** Auto-approve all signing requests (headless e2e). Defaults to false. */\n\treadonly autoApprove?: AutoApprovePolicy;\n\t/** Mount the floating wallet drawer UI. Defaults to true. */\n\treadonly mountUI?: boolean;\n\t/** Wallet display name. Defaults to `'Devstack'`. */\n\treadonly name?: string;\n}\n\nexport interface RegisterDevstackDevWalletResult {\n\treadonly wallet: DevWallet;\n\t/** Unregister the wallet, unmount the UI, and tear down the adapter. */\n\treadonly dispose: () => void;\n}\n\n/** Global marker so tests / the plugin can assert injection happened. */\ndeclare global {\n\t// eslint-disable-next-line no-var\n\tvar __DEV_WALLET_INJECTED__: boolean | undefined;\n\t/** In-flight (or settled) registration promise. Published BEFORE the first\n\t * `await` so concurrent evaluations (HMR / double import) coalesce onto a\n\t * single registration instead of each constructing + registering + mounting\n\t * their own wallet. See `registerDevstackDevWallet`. */\n\t// eslint-disable-next-line no-var\n\tvar __devstackDevWalletPromise__: Promise<RegisterDevstackDevWalletResult> | undefined;\n}\n\n/**\n * Construct + register the devstack dev wallet on the current page and wire\n * the Playwright `connectAs` slot. Idempotent: a second call is a no-op and\n * returns the existing instance.\n *\n * Idempotency is enforced SYNCHRONOUSLY: an in-flight registration promise is\n * published on `globalThis.__devstackDevWalletPromise__` before the first\n * `await`, so two evaluations that race in before the first one finishes both\n * resolve to the SAME wallet instead of double-registering.\n */\nexport function registerDevstackDevWallet(\n\tconfig: RegisterDevstackDevWalletConfig,\n): Promise<RegisterDevstackDevWalletResult> {\n\tconst g = globalThis as {\n\t\t__devstackDevWallet__?: RegisterDevstackDevWalletResult;\n\t\t__devstackDevWalletPromise__?: Promise<RegisterDevstackDevWalletResult>;\n\t};\n\t// Fast path: a prior call already completed.\n\tif (g.__devstackDevWallet__ !== undefined) return Promise.resolve(g.__devstackDevWallet__);\n\t// In-flight path: a prior call is still booting — share its promise. This\n\t// guard runs before any `await`, so it closes the double-init window that a\n\t// post-registration-only marker leaves open under HMR / double import.\n\tif (g.__devstackDevWalletPromise__ !== undefined) return g.__devstackDevWalletPromise__;\n\n\tconst promise = registerDevstackDevWalletImpl(config);\n\tg.__devstackDevWalletPromise__ = promise;\n\t// On failure, clear the in-flight promise so a later call can retry instead\n\t// of being stuck awaiting a rejected registration.\n\tpromise.catch(() => {\n\t\tif (g.__devstackDevWalletPromise__ === promise) {\n\t\t\tg.__devstackDevWalletPromise__ = undefined;\n\t\t}\n\t});\n\treturn promise;\n}\n\nasync function registerDevstackDevWalletImpl(\n\tconfig: RegisterDevstackDevWalletConfig,\n): Promise<RegisterDevstackDevWalletResult> {\n\tconst { mountUI = true, autoApprove = false } = config;\n\tglobalThis.__DEV_WALLET_INJECTED__ = true;\n\n\tconst adapter = new DevstackSignerAdapter({\n\t\tserverOrigin: config.serverOrigin,\n\t\ttoken: config.token ?? null,\n\t\tname: config.name ?? 'Devstack',\n\t});\n\n\t// The wallet EXECUTES (and simulates) `signAndExecuteTransaction` with\n\t// its OWN client — adapter signing is HTTP (server-side keys), but\n\t// execution goes through the wallet's network client. That client MUST\n\t// hit the same routed RPC the app's dApp Kit uses (a raw 127.0.0.1 RPC\n\t// is CORS-blocked from the routed page origin).\n\t//\n\t// Expose a SINGLE network — the active stack's — so the wallet's network\n\t// switcher shows one entry that matches dApp Kit, not a list of unused\n\t// devnet/testnet/mainnet. `mountAndRegisterDevWallet` advertises the\n\t// wallet-standard chain `sui:<network>` derived from this key, matching the\n\t// `sui:<network>` dApp Kit forwards for signing — the `sui:` prefix lives\n\t// only here, at the wallet-standard boundary.\n\tconst rpcUrl = config.rpcUrl ?? 'http://127.0.0.1:9000';\n\tconst activeNetwork = config.network ?? 'localnet';\n\tconst networks: Record<string, string> = { [activeNetwork]: rpcUrl };\n\n\t// Delegate the construct → init-adapter → mount-UI → register → dispose\n\t// sequence to the shared dev-wallet core helper. Two adapters: the\n\t// `DevstackSignerAdapter` brings the stack's server-resolved accounts\n\t// (alice/bob/carol — headless HTTP signing), and a `WebCryptoSignerAdapter`\n\t// lets the user create their OWN accounts in the wallet UI, persisted in\n\t// IndexedDB across reloads (non-extractable WebCrypto keys, NOT in-memory).\n\t// `createInitialAccount` stays off — the devstack adapter already supplies\n\t// accounts, so we never fabricate a throwaway key; the WebCrypto adapter\n\t// starts empty until the user adds one.\n\tconst { wallet, dispose: disposeWallet } = await mountAndRegisterDevWallet({\n\t\tadapters: [adapter, new WebCryptoSignerAdapter()],\n\t\tname: config.name ?? 'Devstack',\n\t\tautoApprove,\n\t\t// Auto-approve `standard:connect` whenever signing is auto-approved — both\n\t\t// are the headless-e2e signal (`DEVSTACK_AUTO_APPROVE`). The test bridge's\n\t\t// explicit `connectWallet(...)` invokes the wallet's connect; without this\n\t\t// it would queue a pending request and block on UI approval that never\n\t\t// comes. In normal dev (no auto-approve) a human approves the connect.\n\t\tautoConnect: Boolean(autoApprove),\n\t\tnetworks,\n\t\tactiveNetwork,\n\t\tmountUI,\n\t});\n\n\tconst result: RegisterDevstackDevWalletResult = {\n\t\twallet,\n\t\tdispose() {\n\t\t\tdisposeWallet();\n\t\t\tglobalThis.__DEV_WALLET_INJECTED__ = false;\n\t\t\tdelete (globalThis as { __devstackDevWallet__?: unknown }).__devstackDevWallet__;\n\t\t\t// Clear the in-flight/settled registration promise too, so a\n\t\t\t// subsequent `registerDevstackDevWallet` re-initializes rather than\n\t\t\t// handing back the disposed instance.\n\t\t\tdelete (globalThis as { __devstackDevWalletPromise__?: unknown })\n\t\t\t\t.__devstackDevWalletPromise__;\n\t\t},\n\t};\n\t(\n\t\tglobalThis as { __devstackDevWallet__?: RegisterDevstackDevWalletResult }\n\t).__devstackDevWallet__ = result;\n\treturn result;\n}\n"],"mappings":";;;;;;;;;;;;;;AAuFA,SAAgB,0BACf,QAC2C;CAC3C,MAAM,IAAI;CAKV,IAAI,EAAE,0BAA0B,QAAW,OAAO,QAAQ,QAAQ,EAAE,qBAAqB;CAIzF,IAAI,EAAE,iCAAiC,QAAW,OAAO,EAAE;CAE3D,MAAM,UAAU,8BAA8B,MAAM;CACpD,EAAE,+BAA+B;CAGjC,QAAQ,YAAY;EACnB,IAAI,EAAE,iCAAiC,SACtC,EAAE,+BAA+B;CAEnC,CAAC;CACD,OAAO;AACR;AAEA,eAAe,8BACd,QAC2C;CAC3C,MAAM,EAAE,UAAU,MAAM,cAAc,UAAU;CAChD,WAAW,0BAA0B;CAErC,MAAM,UAAU,IAAI,sBAAsB;EACzC,cAAc,OAAO;EACrB,OAAO,OAAO,SAAS;EACvB,MAAM,OAAO,QAAQ;CACtB,CAAC;CAcD,MAAM,SAAS,OAAO,UAAU;CAChC,MAAM,gBAAgB,OAAO,WAAW;CACxC,MAAM,WAAmC,GAAG,gBAAgB,OAAO;CAWnE,MAAM,EAAE,QAAQ,SAAS,kBAAkB,MAAM,0BAA0B;EAC1E,UAAU,CAAC,SAAS,IAAI,uBAAuB,CAAC;EAChD,MAAM,OAAO,QAAQ;EACrB;EAMA,aAAa,QAAQ,WAAW;EAChC;EACA;EACA;CACD,CAAC;CAED,MAAM,SAA0C;EAC/C;EACA,UAAU;GACT,cAAc;GACd,WAAW,0BAA0B;GACrC,OAAQ,WAAmD;GAI3D,OAAQ,WACN;EACH;CACD;CACA,WAEE,wBAAwB;CAC1B,OAAO;AACR"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/inject/index.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n//\n// Page-register entry for the devstack dev wallet.\n//\n// This is the single function the devstack Vite plugin injects into a dev\n// page. It builds the token-based `DevstackSignerAdapter` (headless — signs\n// over HTTP, no popup) + the routed-RPC network map, then hands those to the\n// shared `mountAndRegisterDevWallet` core helper, which constructs the\n// `DevWallet`, registers it via wallet-standard (so dApp Kit auto-discovers\n// it), and mounts the floating wallet UI. The ONLY logic that lives here is\n// the devstack-integration glue: the adapter-from-config and the routed-RPC\n// network map.\n//\n// The wallet does NOT pre-connect or seed dApp Kit storage: a fresh page\n// loads disconnected, dApp Kit's own `autoConnect` only re-connects a genuine\n// prior session, and the Playwright `connectAs` helper performs an explicit\n// connection through dApp Kit's public API. That test bridge is registered\n// app-side via `@mysten-incubation/devstack/dapp-kit`'s\n// `registerDAppKitForTesting(dAppKit)` (DEV-only) — it needs the app's dApp\n// Kit instance, which the wallet has no reference to.\n\nimport type { SignerAdapter } from '../types.js';\nimport type { AutoApprovePolicy, DevWallet } from '../wallet/dev-wallet.js';\nimport { mountAndRegisterDevWallet } from '../wallet/mount-and-register.js';\nimport { DevstackSignerAdapter } from '../adapters/devstack-adapter.js';\n\n/** Shape of a single entry in the dev `accounts` map (resolved off the\n * injected deployment envelope via `resolveAccounts()`). Only `address` is\n * consumed here. */\nexport interface DevstackAccountInfo {\n\treadonly address: string;\n\treadonly name?: string;\n}\n\n/** One network the dev wallet operates on — its routed RPC endpoint plus an\n * optional faucet. The wallet advertises every key as the wallet-standard\n * chain `sui:<name>` and routes signing/execution to whichever network dApp\n * Kit has selected; the active network's `faucet` drives a fund flow. The\n * `rpc` MUST be the SAME routed RPC the app's dApp Kit client uses for that\n * network (a raw `127.0.0.1:9000` is CORS-blocked from the routed page\n * origin). The wallet is agnostic to live-vs-local — local and live networks\n * are passed through the same map. */\nexport interface DevstackNetworkInfo {\n\t/** Routed RPC endpoint for this network. */\n\treadonly rpc: string;\n\t/** Optional faucet endpoint for this network (absent on live mainnet /\n\t * fork stacks). */\n\treadonly faucet?: string | null;\n}\n\nexport interface RegisterDevstackDevWalletConfig {\n\t/** Wallet-app origin (the dev-wallet connection's `walletUrl`). */\n\treadonly serverOrigin: string;\n\t/** Bearer pairing token (read by the Vite dev server from the wallet's\n\t * `0o600` side-channel token file), or null. */\n\treadonly token?: string | null;\n\t/** Dev name→account map (resolved off the injected deployment envelope via\n\t * `resolveAccounts()`). */\n\treadonly accounts: Readonly<Record<string, DevstackAccountInfo>>;\n\t/** The FULL network set the app supports — `{ <name>: { rpc, faucet? } }`,\n\t * covering BOTH the live local network and any live (devnet/testnet/…)\n\t * networks the deployment envelope carries. The wallet advertises each as\n\t * the wallet-standard chain `sui:<name>` and operates on whichever one\n\t * dApp Kit has selected, so it persists across a UI `switchNetwork` (only\n\t * the active chain changes — the wallet is registered once). When omitted\n\t * the wallet falls back to a single localnet entry (the non-routed default\n\t * — only correct for a bare localnet setup). */\n\treadonly networks?: Readonly<Record<string, DevstackNetworkInfo>>;\n\t/** The network the wallet opens on (its initial active network). Must be a\n\t * key of `networks`; defaults to the first `networks` key (`'localnet'`\n\t * when `networks` is omitted). */\n\treadonly defaultNetwork?: string;\n\t/** Auto-approve all signing requests (headless e2e). Defaults to false. */\n\treadonly autoApprove?: AutoApprovePolicy;\n\t/** Mount the floating wallet drawer UI. Defaults to true. */\n\treadonly mountUI?: boolean;\n\t/** Wallet display name. Defaults to `'Devstack'`. */\n\treadonly name?: string;\n}\n\nexport interface RegisterDevstackDevWalletResult {\n\treadonly wallet: DevWallet;\n\t/** Unregister the wallet, unmount the UI, and tear down the adapter. */\n\treadonly dispose: () => void;\n}\n\n/** Global marker so tests / the plugin can assert injection happened. */\ndeclare global {\n\t// eslint-disable-next-line no-var\n\tvar __DEV_WALLET_INJECTED__: boolean | undefined;\n\t/** In-flight (or settled) registration promise. Published BEFORE the first\n\t * `await` so concurrent evaluations (HMR / double import) coalesce onto a\n\t * single registration instead of each constructing + registering + mounting\n\t * their own wallet. See `registerDevstackDevWallet`. */\n\t// eslint-disable-next-line no-var\n\tvar __devstackDevWalletPromise__: Promise<RegisterDevstackDevWalletResult> | undefined;\n}\n\n/**\n * Construct + register the devstack dev wallet on the current page and wire\n * the Playwright `connectAs` slot. Idempotent: a second call is a no-op and\n * returns the existing instance.\n *\n * Idempotency is enforced SYNCHRONOUSLY: an in-flight registration promise is\n * published on `globalThis.__devstackDevWalletPromise__` before the first\n * `await`, so two evaluations that race in before the first one finishes both\n * resolve to the SAME wallet instead of double-registering.\n */\nexport function registerDevstackDevWallet(\n\tconfig: RegisterDevstackDevWalletConfig,\n): Promise<RegisterDevstackDevWalletResult> {\n\tconst g = globalThis as {\n\t\t__devstackDevWallet__?: RegisterDevstackDevWalletResult;\n\t\t__devstackDevWalletPromise__?: Promise<RegisterDevstackDevWalletResult>;\n\t};\n\t// Fast path: a prior call already completed.\n\tif (g.__devstackDevWallet__ !== undefined) return Promise.resolve(g.__devstackDevWallet__);\n\t// In-flight path: a prior call is still booting — share its promise. This\n\t// guard runs before any `await`, so it closes the double-init window that a\n\t// post-registration-only marker leaves open under HMR / double import.\n\tif (g.__devstackDevWalletPromise__ !== undefined) return g.__devstackDevWalletPromise__;\n\n\tconst promise = registerDevstackDevWalletImpl(config);\n\tg.__devstackDevWalletPromise__ = promise;\n\t// On failure, clear the in-flight promise so a later call can retry instead\n\t// of being stuck awaiting a rejected registration.\n\tpromise.catch(() => {\n\t\tif (g.__devstackDevWalletPromise__ === promise) {\n\t\t\tg.__devstackDevWalletPromise__ = undefined;\n\t\t}\n\t});\n\treturn promise;\n}\n\nasync function registerDevstackDevWalletImpl(\n\tconfig: RegisterDevstackDevWalletConfig,\n): Promise<RegisterDevstackDevWalletResult> {\n\tconst { mountUI = true, autoApprove = false } = config;\n\tglobalThis.__DEV_WALLET_INJECTED__ = true;\n\n\t// Resolve the FULL network set up front so it can be advertised both on\n\t// the wallet (its `chains`) and on every devstack account (per-account\n\t// `chains`) — fork/custom networks must be signable from both surfaces.\n\tconst networkInfos: Readonly<Record<string, DevstackNetworkInfo>> =\n\t\tconfig.networks !== undefined && Object.keys(config.networks).length > 0\n\t\t\t? config.networks\n\t\t\t: { localnet: { rpc: 'http://127.0.0.1:9000' } };\n\tconst networkNames = Object.keys(networkInfos);\n\n\tconst adapter = new DevstackSignerAdapter({\n\t\tserverOrigin: config.serverOrigin,\n\t\ttoken: config.token ?? null,\n\t\tname: config.name ?? 'Devstack',\n\t\tnetworks: networkNames,\n\t});\n\n\t// The wallet EXECUTES (and simulates) `signAndExecuteTransaction` with\n\t// its OWN client — adapter signing is HTTP (server-side keys), but\n\t// execution goes through the wallet's network client. That client MUST\n\t// hit the same routed RPC the app's dApp Kit uses (a raw 127.0.0.1 RPC\n\t// is CORS-blocked from the routed page origin).\n\t//\n\t// Expose the FULL network set the app supports (local + any live networks\n\t// from the deployment envelope) so the wallet operates on whichever\n\t// network dApp Kit has selected. The wallet is registered ONCE via\n\t// wallet-standard and advertises each key as the wallet-standard chain\n\t// `sui:<network>` (matching the `sui:<network>` dApp Kit forwards for\n\t// signing — the `sui:` prefix lives only at the wallet-standard boundary);\n\t// a UI `switchNetwork` just changes the active chain, it never\n\t// re-registers. The wallet does NOT know live-vs-local — both flow through\n\t// the same map. Omitting `networks` falls back to a single localnet entry\n\t// (the legacy non-routed default — resolved as `networkInfos` above).\n\tconst networks: Record<string, string> = {};\n\tconst faucets: Record<string, string> = {};\n\tfor (const [net, info] of Object.entries(networkInfos)) {\n\t\tnetworks[net] = info.rpc;\n\t\tif (info.faucet !== undefined && info.faucet !== null && info.faucet.length > 0) {\n\t\t\tfaucets[net] = info.faucet;\n\t\t}\n\t}\n\t// The wallet opens on the requested default (the dApp-Kit default network),\n\t// falling back to the first declared network.\n\tconst activeNetwork =\n\t\tconfig.defaultNetwork !== undefined && networks[config.defaultNetwork] !== undefined\n\t\t\t? config.defaultNetwork\n\t\t\t: (Object.keys(networks)[0] ?? 'localnet');\n\n\t// Delegate the construct → init-adapter → mount-UI → register → dispose\n\t// sequence to the shared dev-wallet core helper. The `DevstackSignerAdapter`\n\t// brings the stack's server-resolved accounts (alice/bob/carol — headless\n\t// HTTP signing) and is always present. The optional `WebCryptoSignerAdapter`\n\t// lets the user create their OWN accounts in the wallet UI, persisted in\n\t// IndexedDB across reloads (non-extractable WebCrypto keys, NOT in-memory) —\n\t// but it depends on the OPTIONAL `@mysten/signers` peer. Load it dynamically\n\t// and gate on its presence: an app that doesn't install `@mysten/signers`\n\t// still gets a working dev wallet (just without create-your-own-account)\n\t// instead of a hard inject crash on the unresolved peer. `createInitialAccount`\n\t// stays off — the devstack adapter already supplies accounts, so we never\n\t// fabricate a throwaway key; the WebCrypto adapter starts empty until the\n\t// user adds one.\n\tconst adapters: SignerAdapter[] = [adapter];\n\ttry {\n\t\tconst { WebCryptoSignerAdapter } = await import('../adapters/webcrypto-adapter.js');\n\t\tadapters.push(new WebCryptoSignerAdapter());\n\t} catch (error) {\n\t\tconsole.info(\n\t\t\t'[dev-wallet] @mysten/signers is not installed — WebCrypto account creation is ' +\n\t\t\t\t'disabled. Install @mysten/signers to enable creating your own accounts in the ' +\n\t\t\t\t'dev wallet.',\n\t\t\terror,\n\t\t);\n\t}\n\n\tconst { wallet, dispose: disposeWallet } = await mountAndRegisterDevWallet({\n\t\tadapters,\n\t\tname: config.name ?? 'Devstack',\n\t\tautoApprove,\n\t\t// Auto-approve `standard:connect` whenever signing is auto-approved — both\n\t\t// are the headless-e2e signal (`DEVSTACK_AUTO_APPROVE`). The test bridge's\n\t\t// explicit `connectWallet(...)` invokes the wallet's connect; without this\n\t\t// it would queue a pending request and block on UI approval that never\n\t\t// comes. In normal dev (no auto-approve) a human approves the connect.\n\t\tautoConnect: Boolean(autoApprove),\n\t\tnetworks,\n\t\tfaucets,\n\t\tactiveNetwork,\n\t\tmountUI,\n\t});\n\n\tconst result: RegisterDevstackDevWalletResult = {\n\t\twallet,\n\t\tdispose() {\n\t\t\tdisposeWallet();\n\t\t\tglobalThis.__DEV_WALLET_INJECTED__ = false;\n\t\t\tdelete (globalThis as { __devstackDevWallet__?: unknown }).__devstackDevWallet__;\n\t\t\t// Clear the in-flight/settled registration promise too, so a\n\t\t\t// subsequent `registerDevstackDevWallet` re-initializes rather than\n\t\t\t// handing back the disposed instance.\n\t\t\tdelete (globalThis as { __devstackDevWalletPromise__?: unknown })\n\t\t\t\t.__devstackDevWalletPromise__;\n\t\t},\n\t};\n\t(\n\t\tglobalThis as { __devstackDevWallet__?: RegisterDevstackDevWalletResult }\n\t).__devstackDevWallet__ = result;\n\treturn result;\n}\n"],"mappings":";;;;;;;;;;;;;AA6GA,SAAgB,0BACf,QAC2C;CAC3C,MAAM,IAAI;CAKV,IAAI,EAAE,0BAA0B,QAAW,OAAO,QAAQ,QAAQ,EAAE,qBAAqB;CAIzF,IAAI,EAAE,iCAAiC,QAAW,OAAO,EAAE;CAE3D,MAAM,UAAU,8BAA8B,MAAM;CACpD,EAAE,+BAA+B;CAGjC,QAAQ,YAAY;EACnB,IAAI,EAAE,iCAAiC,SACtC,EAAE,+BAA+B;CAEnC,CAAC;CACD,OAAO;AACR;AAEA,eAAe,8BACd,QAC2C;CAC3C,MAAM,EAAE,UAAU,MAAM,cAAc,UAAU;CAChD,WAAW,0BAA0B;CAKrC,MAAM,eACL,OAAO,aAAa,UAAa,OAAO,KAAK,OAAO,QAAQ,CAAC,CAAC,SAAS,IACpE,OAAO,WACP,EAAE,UAAU,EAAE,KAAK,wBAAwB,EAAE;CACjD,MAAM,eAAe,OAAO,KAAK,YAAY;CAE7C,MAAM,UAAU,IAAI,sBAAsB;EACzC,cAAc,OAAO;EACrB,OAAO,OAAO,SAAS;EACvB,MAAM,OAAO,QAAQ;EACrB,UAAU;CACX,CAAC;CAkBD,MAAM,WAAmC,CAAC;CAC1C,MAAM,UAAkC,CAAC;CACzC,KAAK,MAAM,CAAC,KAAK,SAAS,OAAO,QAAQ,YAAY,GAAG;EACvD,SAAS,OAAO,KAAK;EACrB,IAAI,KAAK,WAAW,UAAa,KAAK,WAAW,QAAQ,KAAK,OAAO,SAAS,GAC7E,QAAQ,OAAO,KAAK;CAEtB;CAGA,MAAM,gBACL,OAAO,mBAAmB,UAAa,SAAS,OAAO,oBAAoB,SACxE,OAAO,iBACN,OAAO,KAAK,QAAQ,CAAC,CAAC,MAAM;CAejC,MAAM,WAA4B,CAAC,OAAO;CAC1C,IAAI;EACH,MAAM,EAAE,2BAA2B,MAAM,OAAO;EAChD,SAAS,KAAK,IAAI,uBAAuB,CAAC;CAC3C,SAAS,OAAO;EACf,QAAQ,KACP,2KAGA,KACD;CACD;CAEA,MAAM,EAAE,QAAQ,SAAS,kBAAkB,MAAM,0BAA0B;EAC1E;EACA,MAAM,OAAO,QAAQ;EACrB;EAMA,aAAa,QAAQ,WAAW;EAChC;EACA;EACA;EACA;CACD,CAAC;CAED,MAAM,SAA0C;EAC/C;EACA,UAAU;GACT,cAAc;GACd,WAAW,0BAA0B;GACrC,OAAQ,WAAmD;GAI3D,OAAQ,WACN;EACH;CACD;CACA,WAEE,wBAAwB;CAC1B,OAAO;AACR"}