@mysten-incubation/dev-wallet 0.5.0 → 0.6.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 (73) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/README.md +12 -8
  3. package/dist/client/connected-apps.d.mts +35 -0
  4. package/dist/client/connected-apps.d.mts.map +1 -0
  5. package/dist/client/connected-apps.mjs +50 -0
  6. package/dist/client/connected-apps.mjs.map +1 -0
  7. package/dist/client/dev-wallet-client.mjs +1 -1
  8. package/dist/client/dev-wallet-client.mjs.map +1 -1
  9. package/dist/client/index.d.mts +2 -1
  10. package/dist/client/index.mjs +2 -1
  11. package/dist/client/request-handler.d.mts +7 -0
  12. package/dist/client/request-handler.d.mts.map +1 -1
  13. package/dist/client/request-handler.mjs +17 -2
  14. package/dist/client/request-handler.mjs.map +1 -1
  15. package/dist/inject/index.d.mts +4 -0
  16. package/dist/inject/index.d.mts.map +1 -1
  17. package/dist/inject/index.mjs +31 -0
  18. package/dist/inject/index.mjs.map +1 -1
  19. package/dist/standalone/assets/{in-memory-adapter-Ban2kRYS.js → in-memory-adapter-uuWG4K4k.js} +1 -1
  20. package/dist/standalone/assets/main-CKYv0eR6.js +7 -0
  21. package/dist/standalone/assets/{webcrypto-adapter-Ddqn-RAu.js → webcrypto-adapter-5ShaPc_M.js} +1 -1
  22. package/dist/standalone/bookmarklet.js +1 -1
  23. package/dist/standalone/index.html +1 -1
  24. package/dist/ui/dev-wallet-balances.d.mts.map +1 -1
  25. package/dist/ui/dev-wallet-balances.mjs +20 -13
  26. package/dist/ui/dev-wallet-balances.mjs.map +1 -1
  27. package/dist/ui/dev-wallet-connect-guide.d.mts +29 -0
  28. package/dist/ui/dev-wallet-connect-guide.d.mts.map +1 -0
  29. package/dist/ui/dev-wallet-connect-guide.mjs +386 -0
  30. package/dist/ui/dev-wallet-connect-guide.mjs.map +1 -0
  31. package/dist/ui/dev-wallet-connected-apps.d.mts +27 -0
  32. package/dist/ui/dev-wallet-connected-apps.d.mts.map +1 -0
  33. package/dist/ui/dev-wallet-connected-apps.mjs +164 -0
  34. package/dist/ui/dev-wallet-connected-apps.mjs.map +1 -0
  35. package/dist/ui/dev-wallet-panel.d.mts.map +1 -1
  36. package/dist/ui/dev-wallet-panel.mjs +0 -9
  37. package/dist/ui/dev-wallet-panel.mjs.map +1 -1
  38. package/dist/ui/dev-wallet-settings.d.mts +8 -2
  39. package/dist/ui/dev-wallet-settings.d.mts.map +1 -1
  40. package/dist/ui/dev-wallet-settings.mjs +86 -171
  41. package/dist/ui/dev-wallet-settings.mjs.map +1 -1
  42. package/dist/ui/dev-wallet-standalone.d.mts +7 -2
  43. package/dist/ui/dev-wallet-standalone.d.mts.map +1 -1
  44. package/dist/ui/dev-wallet-standalone.mjs +68 -222
  45. package/dist/ui/dev-wallet-standalone.mjs.map +1 -1
  46. package/dist/ui/index.d.mts +3 -1
  47. package/dist/ui/index.mjs +3 -1
  48. package/dist/ui/wallet-controller.mjs +15 -3
  49. package/dist/ui/wallet-controller.mjs.map +1 -1
  50. package/dist/wallet/dev-wallet.d.mts +15 -3
  51. package/dist/wallet/dev-wallet.d.mts.map +1 -1
  52. package/dist/wallet/dev-wallet.mjs +94 -30
  53. package/dist/wallet/dev-wallet.mjs.map +1 -1
  54. package/dist/wallet/persisted.mjs +20 -0
  55. package/dist/wallet/persisted.mjs.map +1 -0
  56. package/package.json +6 -6
  57. package/src/app/main.ts +6 -2
  58. package/src/client/connected-apps.ts +79 -0
  59. package/src/client/dev-wallet-client.ts +2 -1
  60. package/src/client/index.ts +2 -0
  61. package/src/client/request-handler.ts +24 -1
  62. package/src/inject/index.ts +55 -0
  63. package/src/ui/dev-wallet-balances.ts +25 -13
  64. package/src/ui/dev-wallet-connect-guide.ts +428 -0
  65. package/src/ui/dev-wallet-connected-apps.ts +191 -0
  66. package/src/ui/dev-wallet-panel.ts +0 -9
  67. package/src/ui/dev-wallet-settings.ts +101 -179
  68. package/src/ui/dev-wallet-standalone.ts +80 -231
  69. package/src/ui/index.ts +2 -0
  70. package/src/ui/wallet-controller.ts +23 -3
  71. package/src/wallet/dev-wallet.ts +130 -39
  72. package/src/wallet/persisted.ts +24 -0
  73. package/dist/standalone/assets/main-xfzarPod.js +0 -7
@@ -1 +1 @@
1
- {"version":3,"file":"wallet-controller.mjs","names":["#wallet","#unsubscribe","#subscribe","#isForkMode","#resolveUpstreamLabel","#unsubscribeEvents","#unsubscribeRequests","#unsubscribeConnect"],"sources":["../../src/ui/wallet-controller.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { ClientWithCoreApi } from '@mysten/sui/client';\nimport type { ReadonlyWalletAccount } from '@mysten/wallet-standard';\nimport { html, nothing } from 'lit';\nimport type { ReactiveController, ReactiveControllerHost } from 'lit';\n\nimport type { ForkRelay } from '../adapters/fork-relay.js';\nimport { getNetworkFromChain } from '../wallet/constants.js';\nimport type {\n\tDevWallet,\n\tPendingConnectRequest,\n\tPendingSigningRequest,\n} from '../wallet/dev-wallet.js';\nimport './dev-wallet-account-selector.js';\nimport './dev-wallet-balances.js';\nimport './dev-wallet-connect.js';\nimport './dev-wallet-fork-panel.js';\nimport './dev-wallet-network-badge.js';\nimport './dev-wallet-objects.js';\nimport './dev-wallet-settings.js';\nimport './dev-wallet-signing-modal.js';\nimport type { TabId } from './dev-wallet-tab-bar.js';\nimport './dev-wallet-tab-bar.js';\nimport type { CoinRecord } from './utils.js';\nimport { isForkNetwork } from './utils.js';\n\n/**\n * Lit Reactive Controller that encapsulates all shared wallet management logic\n * between `dev-wallet-panel` and `dev-wallet-standalone`.\n *\n * Both components delegate state management, event subscription, and shared\n * render fragments to this controller, keeping only layout-specific code.\n */\nexport class WalletController implements ReactiveController {\n\thost: ReactiveControllerHost;\n\n\tactiveTab: TabId = 'assets';\n\taccounts: ReadonlyWalletAccount[] = [];\n\tactiveAccountIndex = 0;\n\tpendingRequest: PendingSigningRequest | null = null;\n\tpendingConnect: PendingConnectRequest | null = null;\n\tbookmarkletOrigin = '';\n\t/** Pre-seeded coin metadata (typically the generated `coins` constant from\n\t * devstack codegen). Forwarded to `<dev-wallet-balances>` and the\n\t * signing modal so they can skip per-coin RPC waterfalls. */\n\tcoins: CoinRecord | null = null;\n\t/** Phase 5 Subtopic 6 — when set, the controller surfaces a Fork\n\t * tab and binds it to this relay. The hosting custom element\n\t * (`dev-wallet-panel`) constructs the relay from the manifest's\n\t * wallet endpoint and pipes it through as a property. */\n\tforkRelay: ForkRelay | null = null;\n\t/** Upstream label (`'mainnet'`, etc.) sourced from the manifest. Used\n\t * by the fork panel banner and by `renderTabBar` to decide whether\n\t * to render the Fork tab. Falls back to inferring from the active\n\t * network literal (`'*-fork'`) when not provided. */\n\tforkUpstream = '';\n\n\t#wallet: DevWallet | null = null;\n\t#unsubscribeEvents: (() => void) | null = null;\n\t#unsubscribeRequests: (() => void) | null = null;\n\t#unsubscribeConnect: (() => void) | null = null;\n\n\tconstructor(host: ReactiveControllerHost) {\n\t\tthis.host = host;\n\t\thost.addController(this);\n\t}\n\n\tget wallet(): DevWallet | null {\n\t\treturn this.#wallet;\n\t}\n\n\tget activeAddress(): string {\n\t\treturn this.accounts[this.activeAccountIndex]?.address ?? '';\n\t}\n\n\tset wallet(value: DevWallet | null) {\n\t\tif (value === this.#wallet) return;\n\t\tthis.#unsubscribe();\n\t\tthis.#wallet = value;\n\t\tthis.#subscribe();\n\t\tthis.syncState();\n\t}\n\n\thostConnected(): void {\n\t\tthis.#subscribe();\n\t\tthis.syncState();\n\t}\n\n\thostDisconnected(): void {\n\t\tthis.#unsubscribe();\n\t}\n\n\t// ── State sync ──────────────────────────────────────────────────────────\n\n\tsyncState(): void {\n\t\tif (!this.#wallet) return;\n\t\tconst newAccounts = this.#wallet.accounts;\n\t\tconst newPending = this.#wallet.pendingRequest;\n\n\t\tconst accountsChanged =\n\t\t\tnewAccounts.length !== this.accounts.length ||\n\t\t\tnewAccounts.some((a, i) => a.address !== this.accounts[i]?.address);\n\t\tconst pendingChanged = newPending !== this.pendingRequest;\n\n\t\tif (!accountsChanged && !pendingChanged) return;\n\n\t\tif (accountsChanged) {\n\t\t\tthis.accounts = [...newAccounts];\n\t\t\t// Clamp activeAccountIndex when accounts shrink\n\t\t\tif (this.activeAccountIndex >= newAccounts.length && newAccounts.length > 0) {\n\t\t\t\tthis.activeAccountIndex = newAccounts.length - 1;\n\t\t\t}\n\t\t}\n\t\tthis.pendingRequest = newPending;\n\t\tthis.host.requestUpdate();\n\t}\n\n\tsyncConnectState(): void {\n\t\tif (!this.#wallet) return;\n\t\tconst newConnect = this.#wallet.pendingConnect;\n\t\tif (newConnect === this.pendingConnect) return;\n\t\tthis.pendingConnect = newConnect;\n\t\tthis.host.requestUpdate();\n\t}\n\n\t// ── Client resolution ───────────────────────────────────────────────────\n\n\tgetActiveClient(): ClientWithCoreApi | null {\n\t\tif (!this.#wallet) return null;\n\n\t\tif (this.pendingRequest) {\n\t\t\tconst network = getNetworkFromChain(this.pendingRequest.chain);\n\t\t\tif (network) {\n\t\t\t\ttry {\n\t\t\t\t\treturn this.#wallet.getClient(network);\n\t\t\t\t} catch {\n\t\t\t\t\treturn this.#wallet.activeClient;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn this.#wallet.activeClient;\n\t}\n\n\t// ── Event handlers ──────────────────────────────────────────────────────\n\n\tasync handleApprove(): Promise<void> {\n\t\tif (!this.#wallet) return;\n\t\ttry {\n\t\t\tawait this.#wallet.approveRequest();\n\t\t} catch (error) {\n\t\t\tconsole.error('[dev-wallet] approve failed:', error);\n\t\t}\n\t}\n\n\thandleReject(): void {\n\t\tif (!this.#wallet) return;\n\t\ttry {\n\t\t\tthis.#wallet.rejectRequest();\n\t\t} catch (error) {\n\t\t\tconsole.error('[dev-wallet] reject failed:', error);\n\t\t}\n\t}\n\n\thandleTabChanged(e: CustomEvent<{ tab: TabId }>): void {\n\t\tthis.activeTab = e.detail.tab;\n\t\tthis.host.requestUpdate();\n\t}\n\n\thandleNetworkChanged(e: CustomEvent<{ network: string }>): void {\n\t\tthis.#wallet?.setActiveNetwork(e.detail.network);\n\t}\n\n\thandleAccountSelected(e: CustomEvent<{ account: ReadonlyWalletAccount }>): void {\n\t\tconst index = this.accounts.findIndex((a) => a.address === e.detail.account.address);\n\t\tif (index !== -1) {\n\t\t\tthis.activeAccountIndex = index;\n\t\t\tthis.host.requestUpdate();\n\t\t}\n\t}\n\n\thandleApproveConnect(e: CustomEvent<{ selectedAddresses: string[] }>): void {\n\t\te.stopPropagation();\n\t\tif (!this.#wallet) return;\n\t\ttry {\n\t\t\tthis.#wallet.approveConnect(e.detail.selectedAddresses);\n\t\t} catch (error) {\n\t\t\tconsole.error('[dev-wallet] connect approve failed:', error);\n\t\t}\n\t}\n\n\thandleRejectConnect(e?: Event): void {\n\t\te?.preventDefault();\n\t\tif (e instanceof CustomEvent) e.stopPropagation();\n\t\tif (!this.#wallet) return;\n\t\ttry {\n\t\t\tthis.#wallet.rejectConnect();\n\t\t} catch (error) {\n\t\t\tconsole.error('[dev-wallet] connect reject failed:', error);\n\t\t}\n\t}\n\n\t// ── Shared render fragments ─────────────────────────────────────────────\n\n\trenderAccountSelector() {\n\t\treturn html`\n\t\t\t<div class=\"section\">\n\t\t\t\t<dev-wallet-account-selector\n\t\t\t\t\texportparts=\"trigger: selector-trigger, copy-button: selector-copy-button, empty-state: selector-empty-state\"\n\t\t\t\t\t.accounts=${this.accounts}\n\t\t\t\t\t.adapters=${this.#wallet ? [...this.#wallet.adapters] : []}\n\t\t\t\t\t.activeAddress=${this.activeAddress}\n\t\t\t\t\t@account-selected=${(e: CustomEvent) => this.handleAccountSelected(e)}\n\t\t\t\t></dev-wallet-account-selector>\n\t\t\t</div>\n\t\t`;\n\t}\n\n\trenderAssetsTab() {\n\t\treturn html`\n\t\t\t${this.renderAccountSelector()}\n\t\t\t${this.activeAddress && this.#wallet\n\t\t\t\t? html`\n\t\t\t\t\t\t<div class=\"section\">\n\t\t\t\t\t\t\t<dev-wallet-balances\n\t\t\t\t\t\t\t\texportparts=\"balance-list, loading: balances-loading, error-message: balances-error-message, empty-state: balances-empty-state, faucet-button: balances-faucet-button, faucet-error-message: balances-faucet-error-message\"\n\t\t\t\t\t\t\t\t.address=${this.activeAddress}\n\t\t\t\t\t\t\t\t.client=${this.getActiveClient()}\n\t\t\t\t\t\t\t\t.network=${this.#wallet.activeNetwork}\n\t\t\t\t\t\t\t\t.faucetHost=${this.#wallet.activeFaucet}\n\t\t\t\t\t\t\t\t.coins=${this.coins}\n\t\t\t\t\t\t\t></dev-wallet-balances>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t`\n\t\t\t\t: nothing}\n\t\t`;\n\t}\n\n\trenderObjectsTab() {\n\t\treturn html`\n\t\t\t${this.renderAccountSelector()}\n\t\t\t${this.activeAddress && this.#wallet\n\t\t\t\t? html`\n\t\t\t\t\t\t<div class=\"section\">\n\t\t\t\t\t\t\t<dev-wallet-objects\n\t\t\t\t\t\t\t\texportparts=\"object-list, loading: objects-loading, error-message: objects-error-message, empty-state: objects-empty-state, load-more-button\"\n\t\t\t\t\t\t\t\t.address=${this.activeAddress}\n\t\t\t\t\t\t\t\t.client=${this.getActiveClient()}\n\t\t\t\t\t\t\t></dev-wallet-objects>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t`\n\t\t\t\t: nothing}\n\t\t`;\n\t}\n\n\trenderSettingsTab() {\n\t\treturn html`\n\t\t\t<dev-wallet-settings\n\t\t\t\texportparts=\"accounts-account-list, accounts-add-button, accounts-empty-state\"\n\t\t\t\t.wallet=${this.#wallet}\n\t\t\t\t.accounts=${this.accounts}\n\t\t\t\t.adapters=${this.#wallet ? [...this.#wallet.adapters] : []}\n\t\t\t\t.activeAddress=${this.activeAddress}\n\t\t\t\t.bookmarkletOrigin=${this.bookmarkletOrigin}\n\t\t\t\t@account-selected=${(e: CustomEvent) => this.handleAccountSelected(e)}\n\t\t\t></dev-wallet-settings>\n\t\t`;\n\t}\n\n\trenderForkTab() {\n\t\t// Defence-in-depth: the tab itself is hidden unless `#isForkMode`\n\t\t// returns true, but render-time we re-check so direct\n\t\t// `activeTab = 'fork'` writes don't end up rendering the panel\n\t\t// against a bundled-mode stack.\n\t\tif (!this.#isForkMode()) return nothing;\n\t\treturn html`\n\t\t\t<dev-wallet-fork-panel\n\t\t\t\texportparts=\"\n\t\t\t\t\tfork-banner,\n\t\t\t\t\tstatus-grid,\n\t\t\t\t\tstatus-checkpoint,\n\t\t\t\t\tstatus-clock,\n\t\t\t\t\tstatus-clock-iso,\n\t\t\t\t\tstatus-auto-tick,\n\t\t\t\t\tstatus-loading,\n\t\t\t\t\tstatus-error,\n\t\t\t\t\tstatus-empty,\n\t\t\t\t\tadvance-clock-input,\n\t\t\t\t\tadvance-clock-button,\n\t\t\t\t\tadvance-checkpoint-input,\n\t\t\t\t\tadvance-checkpoint-button,\n\t\t\t\t\taction-error,\n\t\t\t\t\tslot-list,\n\t\t\t\t\tslot-item,\n\t\t\t\t\tslot-item-active,\n\t\t\t\t\tslot-toggle,\n\t\t\t\t\tslots-empty,\n\t\t\t\t\tslots-error,\n\t\t\t\t\tempty-state\n\t\t\t\t\"\n\t\t\t\t.relay=${this.forkRelay}\n\t\t\t\t.upstream=${this.#resolveUpstreamLabel()}\n\t\t\t\t@fork-impersonation-changed=${(e: CustomEvent) => this.handleForkImpersonationChanged(e)}\n\t\t\t></dev-wallet-fork-panel>\n\t\t`;\n\t}\n\n\trenderTabContent() {\n\t\tif (this.activeTab === 'assets') return this.renderAssetsTab();\n\t\tif (this.activeTab === 'objects') return this.renderObjectsTab();\n\t\tif (this.activeTab === 'fork') return this.renderForkTab();\n\t\tif (this.activeTab === 'settings') return this.renderSettingsTab();\n\t\treturn nothing;\n\t}\n\n\thandleForkImpersonationChanged(_e: CustomEvent): void {\n\t\t// Slot changes can affect the impersonation banner in the signing\n\t\t// modal (Phase 4 P4.19), so request a re-render. The actual flag\n\t\t// flip happens in the signing-modal's own derivation from\n\t\t// `account.source`, which the devstack adapter updates when the\n\t\t// server-side toggle resolves.\n\t\tthis.host.requestUpdate();\n\t}\n\n\trenderSigningModal() {\n\t\tif (!this.pendingRequest) return nothing;\n\n\t\treturn html`\n\t\t\t<dev-wallet-signing-modal\n\t\t\t\texportparts=\"signing-approve-button, signing-reject-button, signing-request-type, signing-empty-state, signing-error-message, signing-footer, dialog: signing-dialog\"\n\t\t\t\t.request=${this.pendingRequest}\n\t\t\t\t.client=${this.getActiveClient()}\n\t\t\t\t.coins=${this.coins}\n\t\t\t\t.walletName=${this.#wallet?.name ?? 'Dev Wallet'}\n\t\t\t\t@approve=${() => this.handleApprove()}\n\t\t\t\t@reject=${() => this.handleReject()}\n\t\t\t></dev-wallet-signing-modal>\n\t\t`;\n\t}\n\n\trenderConnectPicker() {\n\t\tif (!this.pendingConnect) return nothing;\n\n\t\tconst allAccounts = this.#wallet\n\t\t\t? this.#wallet.adapters.flatMap((a) =>\n\t\t\t\t\ta.getAccounts().map((acc) => ({\n\t\t\t\t\t\taddress: acc.address,\n\t\t\t\t\t\tlabel: acc.label,\n\t\t\t\t\t\tadapterName: a.name,\n\t\t\t\t\t})),\n\t\t\t\t)\n\t\t\t: [];\n\n\t\treturn html`\n\t\t\t<dialog class=\"connect-dialog\" @cancel=${(e: Event) => this.handleRejectConnect(e)}>\n\t\t\t\t<div class=\"connect-dialog-header\">\n\t\t\t\t\t<span class=\"connect-dialog-title\">${this.#wallet?.name ?? 'Dev Wallet'}</span>\n\t\t\t\t</div>\n\t\t\t\t<dev-wallet-connect\n\t\t\t\t\texportparts=\"approve-button: connect-approve-button, reject-button: connect-reject-button, account-list: connect-account-list, error-message: connect-error-message\"\n\t\t\t\t\t.accounts=${allAccounts}\n\t\t\t\t\t@approve=${(e: CustomEvent) => this.handleApproveConnect(e)}\n\t\t\t\t\t@reject=${(e: Event) => this.handleRejectConnect(e)}\n\t\t\t\t></dev-wallet-connect>\n\t\t\t</dialog>\n\t\t`;\n\t}\n\n\trenderNetworkBadge() {\n\t\tif (!this.#wallet) return nothing;\n\t\treturn html`\n\t\t\t<dev-wallet-network-badge\n\t\t\t\texportparts=\"trigger: network-trigger\"\n\t\t\t\t.active=${this.#wallet.activeNetwork}\n\t\t\t\t.networks=${this.#wallet.availableNetworks}\n\t\t\t\t@network-changed=${(e: CustomEvent) => this.handleNetworkChanged(e)}\n\t\t\t></dev-wallet-network-badge>\n\t\t`;\n\t}\n\n\trenderTabBar() {\n\t\treturn html`\n\t\t\t<dev-wallet-tab-bar\n\t\t\t\texportparts=\"tab-bar, tab\"\n\t\t\t\t.active=${this.activeTab}\n\t\t\t\t.showFork=${this.#isForkMode()}\n\t\t\t\t@tab-changed=${(e: CustomEvent) => this.handleTabChanged(e)}\n\t\t\t></dev-wallet-tab-bar>\n\t\t`;\n\t}\n\n\t/** Phase 5 Subtopic 6 (P5.8.5) — the Fork tab + panel render only\n\t * when the active stack is in fork mode. Two signals satisfy that:\n\t * 1. The host passed in a `ForkRelay` (built from the manifest's\n\t * wallet endpoint — devstack only constructs one when\n\t * `meta.runtime === 'forked'`).\n\t * 2. The active network literal ends in `-fork` (catch-all for\n\t * when the relay is wired but the user switched to a\n\t * non-fork network from the badge dropdown).\n\t * Both signals must agree — otherwise the tab is hidden. */\n\t#isForkMode(): boolean {\n\t\tif (this.forkRelay === null) return false;\n\t\tconst active = this.#wallet?.activeNetwork;\n\t\tif (!active) return false;\n\t\treturn isForkNetwork(active);\n\t}\n\n\t#resolveUpstreamLabel(): string {\n\t\tif (this.forkUpstream !== '') return this.forkUpstream;\n\t\tconst active = this.#wallet?.activeNetwork ?? '';\n\t\treturn active.endsWith('-fork') ? active.replace(/-fork$/, '') : '';\n\t}\n\n\t// ── Subscriptions ───────────────────────────────────────────────────────\n\n\t#subscribe(): void {\n\t\tif (!this.#wallet) return;\n\t\tthis.#unsubscribeEvents = this.#wallet.features['standard:events'].on('change', () => {\n\t\t\tthis.syncState();\n\t\t});\n\t\tthis.#unsubscribeRequests = this.#wallet.onRequestChange(() => {\n\t\t\tthis.syncState();\n\t\t});\n\t\tthis.#unsubscribeConnect = this.#wallet.onConnectChange(() => {\n\t\t\tthis.syncConnectState();\n\t\t});\n\t}\n\n\t#unsubscribe(): void {\n\t\tthis.#unsubscribeEvents?.();\n\t\tthis.#unsubscribeEvents = null;\n\t\tthis.#unsubscribeRequests?.();\n\t\tthis.#unsubscribeRequests = null;\n\t\tthis.#unsubscribeConnect?.();\n\t\tthis.#unsubscribeConnect = null;\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"wallet-controller.mjs","names":["#wallet","#unsubscribe","#subscribe","#networkKey","#isForkMode","#resolveUpstreamLabel","#unsubscribeEvents","#unsubscribeRequests","#unsubscribeConnect"],"sources":["../../src/ui/wallet-controller.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { ClientWithCoreApi } from '@mysten/sui/client';\nimport type { ReadonlyWalletAccount } from '@mysten/wallet-standard';\nimport { html, nothing } from 'lit';\nimport type { ReactiveController, ReactiveControllerHost } from 'lit';\n\nimport type { ForkRelay } from '../adapters/fork-relay.js';\nimport type { ConnectedAppsStore } from '../client/connected-apps.js';\nimport { getNetworkFromChain } from '../wallet/constants.js';\nimport type {\n\tDevWallet,\n\tPendingConnectRequest,\n\tPendingSigningRequest,\n} from '../wallet/dev-wallet.js';\nimport './dev-wallet-account-selector.js';\nimport './dev-wallet-balances.js';\nimport './dev-wallet-connect.js';\nimport './dev-wallet-fork-panel.js';\nimport './dev-wallet-network-badge.js';\nimport './dev-wallet-objects.js';\nimport './dev-wallet-settings.js';\nimport './dev-wallet-signing-modal.js';\nimport type { TabId } from './dev-wallet-tab-bar.js';\nimport './dev-wallet-tab-bar.js';\nimport type { CoinRecord } from './utils.js';\nimport { isForkNetwork } from './utils.js';\n\n/**\n * Lit Reactive Controller that encapsulates all shared wallet management logic\n * between `dev-wallet-panel` and `dev-wallet-standalone`.\n *\n * Both components delegate state management, event subscription, and shared\n * render fragments to this controller, keeping only layout-specific code.\n */\nexport class WalletController implements ReactiveController {\n\thost: ReactiveControllerHost;\n\n\tactiveTab: TabId = 'assets';\n\taccounts: ReadonlyWalletAccount[] = [];\n\tactiveAccountIndex = 0;\n\tpendingRequest: PendingSigningRequest | null = null;\n\tpendingConnect: PendingConnectRequest | null = null;\n\t/** Origin of a standalone/hosted wallet. When set, Settings leads with the\n\t * connect guide (bookmarklet + dApp Kit snippet). */\n\twalletOrigin = '';\n\t/** Connected-dApps store for a standalone wallet; listed in Settings. */\n\tconnectedApps: ConnectedAppsStore | null = null;\n\t/** Pre-seeded coin metadata (typically the generated `coins` constant from\n\t * devstack codegen). Forwarded to `<dev-wallet-balances>` and the\n\t * signing modal so they can skip per-coin RPC waterfalls. */\n\tcoins: CoinRecord | null = null;\n\t/** Phase 5 Subtopic 6 — when set, the controller surfaces a Fork\n\t * tab and binds it to this relay. The hosting custom element\n\t * (`dev-wallet-panel`) constructs the relay from the manifest's\n\t * wallet endpoint and pipes it through as a property. */\n\tforkRelay: ForkRelay | null = null;\n\t/** Upstream label (`'mainnet'`, etc.) sourced from the manifest. Used\n\t * by the fork panel banner and by `renderTabBar` to decide whether\n\t * to render the Fork tab. Falls back to inferring from the active\n\t * network literal (`'*-fork'`) when not provided. */\n\tforkUpstream = '';\n\n\t#wallet: DevWallet | null = null;\n\t#networkKey = '';\n\t#unsubscribeEvents: (() => void) | null = null;\n\t#unsubscribeRequests: (() => void) | null = null;\n\t#unsubscribeConnect: (() => void) | null = null;\n\n\tconstructor(host: ReactiveControllerHost) {\n\t\tthis.host = host;\n\t\thost.addController(this);\n\t}\n\n\tget wallet(): DevWallet | null {\n\t\treturn this.#wallet;\n\t}\n\n\tget activeAddress(): string {\n\t\treturn this.accounts[this.activeAccountIndex]?.address ?? '';\n\t}\n\n\tset wallet(value: DevWallet | null) {\n\t\tif (value === this.#wallet) return;\n\t\tthis.#unsubscribe();\n\t\tthis.#wallet = value;\n\t\tthis.#subscribe();\n\t\tthis.syncState();\n\t}\n\n\thostConnected(): void {\n\t\tthis.#subscribe();\n\t\tthis.syncState();\n\t}\n\n\thostDisconnected(): void {\n\t\tthis.#unsubscribe();\n\t}\n\n\t// ── State sync ──────────────────────────────────────────────────────────\n\n\tsyncState(): void {\n\t\tif (!this.#wallet) return;\n\t\tconst newAccounts = this.#wallet.accounts;\n\t\tconst newPending = this.#wallet.pendingRequest;\n\n\t\tconst accountsChanged =\n\t\t\tnewAccounts.length !== this.accounts.length ||\n\t\t\tnewAccounts.some((a, i) => a.address !== this.accounts[i]?.address);\n\t\tconst pendingChanged = newPending !== this.pendingRequest;\n\t\t// Network switches and network add/remove only emit `change`; without\n\t\t// this the badge, balances client, and faucet stay on the old network.\n\t\tconst networkKey = `${this.#wallet.activeNetwork}|${this.#wallet.availableNetworks.join(',')}`;\n\t\tconst networkChanged = networkKey !== this.#networkKey;\n\t\tthis.#networkKey = networkKey;\n\n\t\tif (!accountsChanged && !pendingChanged && !networkChanged) return;\n\n\t\tif (accountsChanged) {\n\t\t\tconst hadAccounts = this.accounts.length > 0;\n\t\t\tthis.accounts = [...newAccounts];\n\t\t\t// Restore the persisted active account once accounts first load.\n\t\t\tconst persisted = this.#wallet.activeAccount;\n\t\t\tconst restored = persisted ? newAccounts.findIndex((a) => a.address === persisted) : -1;\n\t\t\tif (!hadAccounts && restored !== -1) {\n\t\t\t\tthis.activeAccountIndex = restored;\n\t\t\t}\n\t\t\t// Clamp activeAccountIndex when accounts shrink\n\t\t\tif (this.activeAccountIndex >= newAccounts.length && newAccounts.length > 0) {\n\t\t\t\tthis.activeAccountIndex = newAccounts.length - 1;\n\t\t\t}\n\t\t}\n\t\tthis.pendingRequest = newPending;\n\t\tthis.host.requestUpdate();\n\t}\n\n\tsyncConnectState(): void {\n\t\tif (!this.#wallet) return;\n\t\tconst newConnect = this.#wallet.pendingConnect;\n\t\tif (newConnect === this.pendingConnect) return;\n\t\tthis.pendingConnect = newConnect;\n\t\tthis.host.requestUpdate();\n\t}\n\n\t// ── Client resolution ───────────────────────────────────────────────────\n\n\tgetActiveClient(): ClientWithCoreApi | null {\n\t\tif (!this.#wallet) return null;\n\n\t\tif (this.pendingRequest) {\n\t\t\tconst network = getNetworkFromChain(this.pendingRequest.chain);\n\t\t\tif (network) {\n\t\t\t\ttry {\n\t\t\t\t\treturn this.#wallet.getClient(network);\n\t\t\t\t} catch {\n\t\t\t\t\treturn this.#wallet.activeClient;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn this.#wallet.activeClient;\n\t}\n\n\t// ── Event handlers ──────────────────────────────────────────────────────\n\n\tasync handleApprove(): Promise<void> {\n\t\tif (!this.#wallet) return;\n\t\ttry {\n\t\t\tawait this.#wallet.approveRequest();\n\t\t} catch (error) {\n\t\t\tconsole.error('[dev-wallet] approve failed:', error);\n\t\t}\n\t}\n\n\thandleReject(): void {\n\t\tif (!this.#wallet) return;\n\t\ttry {\n\t\t\tthis.#wallet.rejectRequest();\n\t\t} catch (error) {\n\t\t\tconsole.error('[dev-wallet] reject failed:', error);\n\t\t}\n\t}\n\n\thandleTabChanged(e: CustomEvent<{ tab: TabId }>): void {\n\t\tthis.activeTab = e.detail.tab;\n\t\tthis.host.requestUpdate();\n\t}\n\n\thandleNetworkChanged(e: CustomEvent<{ network: string }>): void {\n\t\tthis.#wallet?.setActiveNetwork(e.detail.network);\n\t}\n\n\thandleAccountSelected(e: CustomEvent<{ account: ReadonlyWalletAccount }>): void {\n\t\tconst index = this.accounts.findIndex((a) => a.address === e.detail.account.address);\n\t\tif (index !== -1) {\n\t\t\tthis.activeAccountIndex = index;\n\t\t\tthis.#wallet?.setActiveAccount(e.detail.account.address);\n\t\t\tthis.host.requestUpdate();\n\t\t}\n\t}\n\n\thandleApproveConnect(e: CustomEvent<{ selectedAddresses: string[] }>): void {\n\t\te.stopPropagation();\n\t\tif (!this.#wallet) return;\n\t\ttry {\n\t\t\tthis.#wallet.approveConnect(e.detail.selectedAddresses);\n\t\t} catch (error) {\n\t\t\tconsole.error('[dev-wallet] connect approve failed:', error);\n\t\t}\n\t}\n\n\thandleRejectConnect(e?: Event): void {\n\t\te?.preventDefault();\n\t\tif (e instanceof CustomEvent) e.stopPropagation();\n\t\tif (!this.#wallet) return;\n\t\ttry {\n\t\t\tthis.#wallet.rejectConnect();\n\t\t} catch (error) {\n\t\t\tconsole.error('[dev-wallet] connect reject failed:', error);\n\t\t}\n\t}\n\n\t// ── Shared render fragments ─────────────────────────────────────────────\n\n\trenderAccountSelector() {\n\t\treturn html`\n\t\t\t<div class=\"section\">\n\t\t\t\t<dev-wallet-account-selector\n\t\t\t\t\texportparts=\"trigger: selector-trigger, copy-button: selector-copy-button, empty-state: selector-empty-state\"\n\t\t\t\t\t.accounts=${this.accounts}\n\t\t\t\t\t.adapters=${this.#wallet ? [...this.#wallet.adapters] : []}\n\t\t\t\t\t.activeAddress=${this.activeAddress}\n\t\t\t\t\t@account-selected=${(e: CustomEvent) => this.handleAccountSelected(e)}\n\t\t\t\t></dev-wallet-account-selector>\n\t\t\t</div>\n\t\t`;\n\t}\n\n\trenderAssetsTab() {\n\t\treturn html`\n\t\t\t${this.renderAccountSelector()}\n\t\t\t${this.activeAddress && this.#wallet\n\t\t\t\t? html`\n\t\t\t\t\t\t<div class=\"section\">\n\t\t\t\t\t\t\t<dev-wallet-balances\n\t\t\t\t\t\t\t\texportparts=\"balance-list, loading: balances-loading, error-message: balances-error-message, empty-state: balances-empty-state, faucet-button: balances-faucet-button, faucet-error-message: balances-faucet-error-message\"\n\t\t\t\t\t\t\t\t.address=${this.activeAddress}\n\t\t\t\t\t\t\t\t.client=${this.getActiveClient()}\n\t\t\t\t\t\t\t\t.network=${this.#wallet.activeNetwork}\n\t\t\t\t\t\t\t\t.faucetHost=${this.#wallet.activeFaucet}\n\t\t\t\t\t\t\t\t.coins=${this.coins}\n\t\t\t\t\t\t\t></dev-wallet-balances>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t`\n\t\t\t\t: nothing}\n\t\t`;\n\t}\n\n\trenderObjectsTab() {\n\t\treturn html`\n\t\t\t${this.renderAccountSelector()}\n\t\t\t${this.activeAddress && this.#wallet\n\t\t\t\t? html`\n\t\t\t\t\t\t<div class=\"section\">\n\t\t\t\t\t\t\t<dev-wallet-objects\n\t\t\t\t\t\t\t\texportparts=\"object-list, loading: objects-loading, error-message: objects-error-message, empty-state: objects-empty-state, load-more-button\"\n\t\t\t\t\t\t\t\t.address=${this.activeAddress}\n\t\t\t\t\t\t\t\t.client=${this.getActiveClient()}\n\t\t\t\t\t\t\t></dev-wallet-objects>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t`\n\t\t\t\t: nothing}\n\t\t`;\n\t}\n\n\trenderSettingsTab() {\n\t\treturn html`\n\t\t\t<dev-wallet-settings\n\t\t\t\texportparts=\"accounts-account-list, accounts-add-button, accounts-empty-state\"\n\t\t\t\t.wallet=${this.#wallet}\n\t\t\t\t.accounts=${this.accounts}\n\t\t\t\t.adapters=${this.#wallet ? [...this.#wallet.adapters] : []}\n\t\t\t\t.activeAddress=${this.activeAddress}\n\t\t\t\t.walletOrigin=${this.walletOrigin}\n\t\t\t\t.connectedApps=${this.connectedApps}\n\t\t\t\t@account-selected=${(e: CustomEvent) => this.handleAccountSelected(e)}\n\t\t\t></dev-wallet-settings>\n\t\t`;\n\t}\n\n\trenderForkTab() {\n\t\t// Defence-in-depth: the tab itself is hidden unless `#isForkMode`\n\t\t// returns true, but render-time we re-check so direct\n\t\t// `activeTab = 'fork'` writes don't end up rendering the panel\n\t\t// against a bundled-mode stack.\n\t\tif (!this.#isForkMode()) return nothing;\n\t\treturn html`\n\t\t\t<dev-wallet-fork-panel\n\t\t\t\texportparts=\"\n\t\t\t\t\tfork-banner,\n\t\t\t\t\tstatus-grid,\n\t\t\t\t\tstatus-checkpoint,\n\t\t\t\t\tstatus-clock,\n\t\t\t\t\tstatus-clock-iso,\n\t\t\t\t\tstatus-auto-tick,\n\t\t\t\t\tstatus-loading,\n\t\t\t\t\tstatus-error,\n\t\t\t\t\tstatus-empty,\n\t\t\t\t\tadvance-clock-input,\n\t\t\t\t\tadvance-clock-button,\n\t\t\t\t\tadvance-checkpoint-input,\n\t\t\t\t\tadvance-checkpoint-button,\n\t\t\t\t\taction-error,\n\t\t\t\t\tslot-list,\n\t\t\t\t\tslot-item,\n\t\t\t\t\tslot-item-active,\n\t\t\t\t\tslot-toggle,\n\t\t\t\t\tslots-empty,\n\t\t\t\t\tslots-error,\n\t\t\t\t\tempty-state\n\t\t\t\t\"\n\t\t\t\t.relay=${this.forkRelay}\n\t\t\t\t.upstream=${this.#resolveUpstreamLabel()}\n\t\t\t\t@fork-impersonation-changed=${(e: CustomEvent) => this.handleForkImpersonationChanged(e)}\n\t\t\t></dev-wallet-fork-panel>\n\t\t`;\n\t}\n\n\trenderTabContent() {\n\t\tif (this.activeTab === 'assets') return this.renderAssetsTab();\n\t\tif (this.activeTab === 'objects') return this.renderObjectsTab();\n\t\tif (this.activeTab === 'fork') return this.renderForkTab();\n\t\tif (this.activeTab === 'settings') return this.renderSettingsTab();\n\t\treturn nothing;\n\t}\n\n\thandleForkImpersonationChanged(_e: CustomEvent): void {\n\t\t// Slot changes can affect the impersonation banner in the signing\n\t\t// modal (Phase 4 P4.19), so request a re-render. The actual flag\n\t\t// flip happens in the signing-modal's own derivation from\n\t\t// `account.source`, which the devstack adapter updates when the\n\t\t// server-side toggle resolves.\n\t\tthis.host.requestUpdate();\n\t}\n\n\trenderSigningModal() {\n\t\tif (!this.pendingRequest) return nothing;\n\n\t\treturn html`\n\t\t\t<dev-wallet-signing-modal\n\t\t\t\texportparts=\"signing-approve-button, signing-reject-button, signing-request-type, signing-empty-state, signing-error-message, signing-footer, dialog: signing-dialog\"\n\t\t\t\t.request=${this.pendingRequest}\n\t\t\t\t.client=${this.getActiveClient()}\n\t\t\t\t.coins=${this.coins}\n\t\t\t\t.walletName=${this.#wallet?.name ?? 'Dev Wallet'}\n\t\t\t\t@approve=${() => this.handleApprove()}\n\t\t\t\t@reject=${() => this.handleReject()}\n\t\t\t></dev-wallet-signing-modal>\n\t\t`;\n\t}\n\n\trenderConnectPicker() {\n\t\tif (!this.pendingConnect) return nothing;\n\n\t\tconst allAccounts = this.#wallet\n\t\t\t? this.#wallet.adapters.flatMap((a) =>\n\t\t\t\t\ta.getAccounts().map((acc) => ({\n\t\t\t\t\t\taddress: acc.address,\n\t\t\t\t\t\tlabel: acc.label,\n\t\t\t\t\t\tadapterName: a.name,\n\t\t\t\t\t})),\n\t\t\t\t)\n\t\t\t: [];\n\n\t\treturn html`\n\t\t\t<dialog class=\"connect-dialog\" @cancel=${(e: Event) => this.handleRejectConnect(e)}>\n\t\t\t\t<div class=\"connect-dialog-header\">\n\t\t\t\t\t<span class=\"connect-dialog-title\">${this.#wallet?.name ?? 'Dev Wallet'}</span>\n\t\t\t\t</div>\n\t\t\t\t<dev-wallet-connect\n\t\t\t\t\texportparts=\"approve-button: connect-approve-button, reject-button: connect-reject-button, account-list: connect-account-list, error-message: connect-error-message\"\n\t\t\t\t\t.accounts=${allAccounts}\n\t\t\t\t\t@approve=${(e: CustomEvent) => this.handleApproveConnect(e)}\n\t\t\t\t\t@reject=${(e: Event) => this.handleRejectConnect(e)}\n\t\t\t\t></dev-wallet-connect>\n\t\t\t</dialog>\n\t\t`;\n\t}\n\n\trenderNetworkBadge() {\n\t\tif (!this.#wallet) return nothing;\n\t\treturn html`\n\t\t\t<dev-wallet-network-badge\n\t\t\t\texportparts=\"trigger: network-trigger\"\n\t\t\t\t.active=${this.#wallet.activeNetwork}\n\t\t\t\t.networks=${this.#wallet.availableNetworks}\n\t\t\t\t@network-changed=${(e: CustomEvent) => this.handleNetworkChanged(e)}\n\t\t\t></dev-wallet-network-badge>\n\t\t`;\n\t}\n\n\trenderTabBar() {\n\t\treturn html`\n\t\t\t<dev-wallet-tab-bar\n\t\t\t\texportparts=\"tab-bar, tab\"\n\t\t\t\t.active=${this.activeTab}\n\t\t\t\t.showFork=${this.#isForkMode()}\n\t\t\t\t@tab-changed=${(e: CustomEvent) => this.handleTabChanged(e)}\n\t\t\t></dev-wallet-tab-bar>\n\t\t`;\n\t}\n\n\t/** Phase 5 Subtopic 6 (P5.8.5) — the Fork tab + panel render only\n\t * when the active stack is in fork mode. Two signals satisfy that:\n\t * 1. The host passed in a `ForkRelay` (built from the manifest's\n\t * wallet endpoint — devstack only constructs one when\n\t * `meta.runtime === 'forked'`).\n\t * 2. The active network literal ends in `-fork` (catch-all for\n\t * when the relay is wired but the user switched to a\n\t * non-fork network from the badge dropdown).\n\t * Both signals must agree — otherwise the tab is hidden. */\n\t#isForkMode(): boolean {\n\t\tif (this.forkRelay === null) return false;\n\t\tconst active = this.#wallet?.activeNetwork;\n\t\tif (!active) return false;\n\t\treturn isForkNetwork(active);\n\t}\n\n\t#resolveUpstreamLabel(): string {\n\t\tif (this.forkUpstream !== '') return this.forkUpstream;\n\t\tconst active = this.#wallet?.activeNetwork ?? '';\n\t\treturn active.endsWith('-fork') ? active.replace(/-fork$/, '') : '';\n\t}\n\n\t// ── Subscriptions ───────────────────────────────────────────────────────\n\n\t#subscribe(): void {\n\t\tif (!this.#wallet) return;\n\t\tthis.#unsubscribeEvents = this.#wallet.features['standard:events'].on('change', () => {\n\t\t\tthis.syncState();\n\t\t});\n\t\tthis.#unsubscribeRequests = this.#wallet.onRequestChange(() => {\n\t\t\tthis.syncState();\n\t\t});\n\t\tthis.#unsubscribeConnect = this.#wallet.onConnectChange(() => {\n\t\t\tthis.syncConnectState();\n\t\t});\n\t}\n\n\t#unsubscribe(): void {\n\t\tthis.#unsubscribeEvents?.();\n\t\tthis.#unsubscribeEvents = null;\n\t\tthis.#unsubscribeRequests?.();\n\t\tthis.#unsubscribeRequests = null;\n\t\tthis.#unsubscribeConnect?.();\n\t\tthis.#unsubscribeConnect = null;\n\t}\n}\n"],"mappings":"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"}
@@ -4,6 +4,8 @@ import { ReadonlyWalletAccount, StandardConnectFeature, StandardDisconnectFeatur
4
4
  import { ClientWithCoreApi } from "@mysten/sui/client";
5
5
 
6
6
  //#region src/wallet/dev-wallet.d.ts
7
+ /** localStorage key for {@link PersistedStateV1}. Bump the suffix on shape changes. */
8
+ declare const STATE_STORAGE_KEY = "dev-wallet:state:v1";
7
9
  /**
8
10
  * Default gRPC endpoint URLs for standard Sui networks (devnet, testnet, localnet).
9
11
  */
@@ -69,10 +71,11 @@ interface DevWalletConfig {
69
71
  /** Factory to create a client for a given network. Defaults to SuiGrpcClient. */
70
72
  clientFactory?: (network: string, url: string) => ClientWithCoreApi;
71
73
  /**
72
- * Persist network URLs to localStorage so custom configurations survive page reloads
73
- * and are shared with popup windows.
74
+ * Persist network URLs, the active network, and the UI's active account to
75
+ * localStorage so they survive page reloads and are shared with popup windows.
76
+ * Persisted values take precedence over `networks` and `activeNetwork`.
74
77
  */
75
- persistNetworks?: boolean;
78
+ persistState?: boolean;
76
79
  }
77
80
  /**
78
81
  * wallet-standard Wallet implementation for development. All signing requests are queued
@@ -130,6 +133,15 @@ declare class DevWallet implements Wallet {
130
133
  get availableNetworks(): string[];
131
134
  /** Switch the active network. Emits a `change` event. */
132
135
  setActiveNetwork(network: string): void;
136
+ /** Address the wallet UI shows as active, or `null` when unset. Persisted
137
+ * with `persistState`. Unlike {@link setSelectedAccount}, this doesn't
138
+ * change which accounts dApps see. */
139
+ get activeAccount(): string | null;
140
+ setActiveAccount(address: string | null): void;
141
+ /**
142
+ * Add a network or replace its URL. `faucet` sets the network's faucet
143
+ * endpoint; pass `null` to remove it, or omit it to leave it unchanged.
144
+ */
133
145
  addNetwork(name: string, url: string, faucet?: string | null): void;
134
146
  removeNetwork(name: string): void;
135
147
  /** Register this wallet with the wallet-standard registry. Returns an unregister function. */
@@ -1 +1 @@
1
- {"version":3,"file":"dev-wallet.d.mts","names":[],"sources":["../../src/wallet/dev-wallet.ts"],"mappings":";;;;;;;AAkCA;;cAAa,oBAAA;EAAA;;;;;UAOI,aAAA;EAChB,EAAA;EACA,IAAA;EACA,OAAA,EAAS,aAAA;EACT,KAAA;EAEe;EAAf,IAAA,WAAe,UAAA;EACf,OAAA,GAAU,MAAA,EAAQ,aAAA;EAClB,MAAA,GAAS,KAAA,EAAO,KAAA;AAAA;;KAIL,qBAAA,GAAwB,IAAI,CAAC,aAAA;AAAA,UAExB,cAAA;EAChB,EAAA;EACA,OAAA,GAAU,MAAA;IAAU,QAAA,WAAmB,aAAA;EAAA;EACvC,MAAA,GAAS,KAAA,EAAO,KAAK;AAAA;;KAIV,qBAAA,GAAwB,IAAI,CAAC,cAAA;;;;AAbnB;KAmBV,iBAAA,eAEP,OAAA;EACF,IAAA,EAAM,aAAA;EACN,OAAA,EAAS,aAAA;EACT,KAAA;EACA,IAAA,WAAe,UAAA;AAAA;AAAA,UAGD,eAAA;EAChB,QAAA,EAAU,aAAA;EApBW;EAsBrB,QAAA,GAAW,MAAA;EAvBX;;;;;EA6BA,OAAA,GAAU,MAAA;EA5BD;EA8BT,IAAA;EACA,IAAA,GAAO,UAAA;EA3BI;EA6BX,aAAA;;;AA7BsD;AAMvD;EA4BC,WAAA,GAAc,iBAAA;;EAEd,WAAA;EA1BW;EA4BX,aAAA,IAAiB,OAAA,UAAiB,GAAA,aAAgB,iBAAA;EA1BvB;;;;EA+B3B,eAAA;AAAA;;;;;cAOY,SAAA,YAAqB,MAAA;EAAA;cA4BrB,MAAA,EAAQ,eAAA;EAAA,IA4BhB,OAAA;EAAA,IAIA,IAAA;EAAA,IAIA,IAAA;EAhGO;;;;;;;;EAAA,IA4GP,MAAA;EAAA,IAIA,QAAA,aAAqB,qBAAA;EAhHd;;;;;;;;;;;EA+HX,kBAAA,CAAmB,OAAA;EAAA,IAwBf,QAAA,IAAY,sBAAA,GACf,qBAAA,GACA,yBAAA,GACA,WAAA;EAtIiD;EAAA,IAoK9C,cAAA,IAAkB,qBAAA;EAAA,IAIlB,QAAA,aAAqB,aAAA;EAIzB,oBAAA,CAAqB,OAAA,WAAkB,aAAA;EAhK3B;EAAA,IAqKR,OAAA,IAAW,MAAA,SAAe,iBAAA;EAO9B,SAAA,CAAU,OAAA,WAAkB,iBAAA;EAAA,IAIxB,WAAA,IAAe,MAAA;EAhGM;;EAAA,IAsGrB,UAAA,IAAc,MAAA;EA7DjB;;EAmED,SAAA,CAAU,OAAA;EAhCe;;;EAAA,IAuCrB,YAAA;EAAA,IAIA,aAAA;EAAA,IAIA,YAAA,IAAgB,iBAAA;EAAA,IAKhB,iBAAA;EALgB;EAUpB,gBAAA,CAAiB,OAAA;EAUjB,UAAA,CAAW,IAAA,UAAc,GAAA,UAAa,MAAA;EAmBtC,aAAA,CAAc,IAAA;EAyHsB;EA7GpC,QAAA;EA9PuC;EAyQvC,OAAA;EAzQiC;EAiS3B,cAAA,IAAkB,OAAA;EAuBxB,aAAA,CAAc,MAAA;EA5RM;EAuSpB,eAAA,CAAgB,QAAA,GAAW,OAAA,EAAS,qBAAA;EA3QhC;EAAA,IAmRA,cAAA,IAAkB,qBAAA;EA3QlB;EAgRJ,cAAA,CAAe,iBAAA;EAgBf,aAAA,CAAc,MAAA;EAhRW;EA2RzB,eAAA,CAAgB,QAAA,GAAW,OAAA,EAAS,qBAAA;EA5QjB;;;;;;EAuSnB,eAAA,CAAgB,QAAA;AAAA"}
1
+ {"version":3,"file":"dev-wallet.d.mts","names":[],"sources":["../../src/wallet/dev-wallet.ts"],"mappings":";;;;;;;cA+Ba,iBAAA;;;;cAoEA,oBAAA;EAAA;;;;;UAOI,aAAA;EAChB,EAAA;EACA,IAAA;EACA,OAAA,EAAS,aAAA;EACT,KAAA;EAJ6B;EAM7B,IAAA,WAAe,UAAA;EACf,OAAA,GAAU,MAAA,EAAQ,aAAA;EAClB,MAAA,GAAS,KAAA,EAAO,KAAA;AAAA;;KAIL,qBAAA,GAAwB,IAAI,CAAC,aAAA;AAAA,UAExB,cAAA;EAChB,EAAA;EACA,OAAA,GAAU,MAAA;IAAU,QAAA,WAAmB,aAAA;EAAA;EACvC,MAAA,GAAS,KAAA,EAAO,KAAK;AAAA;;KAIV,qBAAA,GAAwB,IAAI,CAAC,cAAA;;;;;KAM7B,iBAAA,eAEP,OAAA;EACF,IAAA,EAAM,aAAA;EACN,OAAA,EAAS,aAAA;EACT,KAAA;EACA,IAAA,WAAe,UAAA;AAAA;AAAA,UAGD,eAAA;EAChB,QAAA,EAAU,aAAA;EAvBM;EAyBhB,QAAA,GAAW,MAAA;;;;;;EAMX,OAAA,GAAU,MAAA;EA7BA;EA+BV,IAAA;EACA,IAAA,GAAO,UAAA;EA/BE;EAiCT,aAAA;EAjCqB;AAItB;;;EAkCC,WAAA,GAAc,iBAAA;EAlCwC;EAoCtD,WAAA;EA9B4B;EAgC5B,aAAA,IAAiB,OAAA,UAAiB,GAAA,aAAgB,iBAAA;EA7B1C;;;;;EAmCR,YAAA;AAAA;;;;;cAOY,SAAA,YAAqB,MAAA;EAAA;cA+BrB,MAAA,EAAQ,eAAA;EAAA,IAkChB,OAAA;EAAA,IAIA,IAAA;EAAA,IAIA,IAAA;;;;;;;;;MAYA,MAAA;EAAA,IAIA,QAAA,aAAqB,qBAAA;EA5Hf;;;;;;;;;;;EA2IV,kBAAA,CAAmB,OAAA;EAAA,IAwBf,QAAA,IAAY,sBAAA,GACf,qBAAA,GACA,yBAAA,GACA,WAAA;EAhJgB;EAAA,IA8Kb,cAAA,IAAkB,qBAAA;EAAA,IAIlB,QAAA,aAAqB,aAAA;EAIzB,oBAAA,CAAqB,OAAA,WAAkB,aAAA;EAhL3B;EAAA,IAqLR,OAAA,IAAW,MAAA,SAAe,iBAAA;EAO9B,SAAA,CAAU,OAAA,WAAkB,iBAAA;EAAA,IAIxB,WAAA,IAAe,MAAA;;;MAMf,UAAA,IAAc,MAAA;EA/DF;;EAqEhB,SAAA,CAAU,OAAA;EAlET;;;EAAA,IAyEG,YAAA;EAAA,IAIA,aAAA;EAAA,IAIA,YAAA,IAAgB,iBAAA;EAAA,IAKhB,iBAAA;EAhCe;EAqCnB,gBAAA,CAAiB,OAAA;EAVG;;;EAAA,IAwBhB,aAAA;EAIJ,gBAAA,CAAiB,OAAA;EAhPgB;;;;EAyPjC,UAAA,CAAW,IAAA,UAAc,GAAA,UAAa,MAAA;EActC,aAAA,CAAc,IAAA;EAxOM;EAoPpB,QAAA;EAlNI;EA6NJ,OAAA;EArNI;EA6OE,cAAA,IAAkB,OAAA;EAuBxB,aAAA,CAAc,MAAA;EApPW;EA+PzB,eAAA,CAAgB,QAAA,GAAW,OAAA,EAAS,qBAAA;EAhPjB;EAAA,IAwPf,cAAA,IAAkB,qBAAA;EAhON;EAqOhB,cAAA,CAAe,iBAAA;EAgBf,aAAA,CAAc,MAAA;EAlPb;EA6PD,eAAA,CAAgB,QAAA,GAAW,OAAA,EAAS,qBAAA;EA/Nd;;;;;;EA0PtB,eAAA,CAAgB,QAAA;AAAA"}
@@ -1,12 +1,52 @@
1
1
  import { chainsForNetworks } from "../adapters/build-managed-account.mjs";
2
2
  import { getNetworkFromChain } from "./constants.mjs";
3
+ import { readPersisted } from "./persisted.mjs";
3
4
  import { executeSigning } from "./signing.mjs";
4
5
  import { SuiGrpcClient } from "@mysten/sui/grpc";
5
6
  import { mitt } from "@mysten/utils";
6
7
  import { getWallets } from "@mysten/wallet-standard";
7
8
  //#region src/wallet/dev-wallet.ts
8
9
  const DEFAULT_WALLET_NAME = "Dev Wallet";
9
- const NETWORK_STORAGE_KEY = "dev-wallet:networks";
10
+ /** localStorage key for {@link PersistedStateV1}. Bump the suffix on shape changes. */
11
+ const STATE_STORAGE_KEY = "dev-wallet:state:v1";
12
+ function isStringRecord(value) {
13
+ return typeof value === "object" && value !== null && !Array.isArray(value) && Object.values(value).every((v) => typeof v === "string");
14
+ }
15
+ /** Throws unless `url` is an http(s) URL. `what` names it in the error, e.g. `network "localnet"`. */
16
+ function assertHttpUrl(url, what) {
17
+ let parsed;
18
+ try {
19
+ parsed = new URL(url);
20
+ } catch {
21
+ throw new Error(`Invalid URL for ${what}: ${url}`);
22
+ }
23
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw new Error(`Invalid URL for ${what}: must use http or https (got ${parsed.protocol})`);
24
+ }
25
+ /** Config faucets overlaid with persisted ones; `''` marks a removed faucet. */
26
+ function mergeFaucets(config, persisted) {
27
+ const merged = {
28
+ ...config,
29
+ ...persisted
30
+ };
31
+ for (const [name, url] of Object.entries(merged)) if (url === "") delete merged[name];
32
+ return merged;
33
+ }
34
+ function decodePersistedState(parsed) {
35
+ if (typeof parsed !== "object" || parsed === null) return null;
36
+ const { version, networks, faucets, activeNetwork, activeAccount } = parsed;
37
+ if (version !== 1) return null;
38
+ if (!isStringRecord(networks)) return null;
39
+ if (faucets !== void 0 && !isStringRecord(faucets)) return null;
40
+ if (activeNetwork !== void 0 && typeof activeNetwork !== "string") return null;
41
+ if (activeAccount !== void 0 && typeof activeAccount !== "string") return null;
42
+ return {
43
+ version,
44
+ networks,
45
+ faucets,
46
+ activeNetwork,
47
+ activeAccount
48
+ };
49
+ }
10
50
  /**
11
51
  * Default gRPC endpoint URLs for standard Sui networks (devnet, testnet, localnet).
12
52
  */
@@ -23,6 +63,8 @@ var DevWallet = class {
23
63
  #adapters;
24
64
  #networkUrls;
25
65
  #faucetUrls;
66
+ /** Faucets from config; persisted faucets layer over these. */
67
+ #configFaucets;
26
68
  #clients;
27
69
  #activeNetwork;
28
70
  #name;
@@ -30,7 +72,8 @@ var DevWallet = class {
30
72
  #autoApprove;
31
73
  #autoConnect;
32
74
  #clientFactory;
33
- #persistNetworks;
75
+ #persistState;
76
+ #activeAccount;
34
77
  #accounts;
35
78
  #selectedAddress = null;
36
79
  #events;
@@ -49,11 +92,14 @@ var DevWallet = class {
49
92
  constructor(config) {
50
93
  if (config.adapters.length === 0) console.warn("[dev-wallet] No adapters provided. The wallet will have no accounts.");
51
94
  this.#adapters = config.adapters;
52
- this.#persistNetworks = config.persistNetworks ?? false;
53
- this.#networkUrls = this.#loadNetworkUrls(config.networks ?? DEFAULT_NETWORK_URLS);
54
- this.#faucetUrls = { ...config.faucets };
95
+ this.#persistState = config.persistState ?? false;
96
+ const persisted = this.#loadState();
97
+ this.#networkUrls = persisted?.networks ?? { ...config.networks ?? DEFAULT_NETWORK_URLS };
98
+ this.#configFaucets = { ...config.faucets };
99
+ this.#faucetUrls = mergeFaucets(this.#configFaucets, persisted?.faucets);
55
100
  this.#clients = {};
56
- this.#activeNetwork = config.activeNetwork ?? Object.keys(this.#networkUrls)[0] ?? "";
101
+ this.#activeNetwork = persisted?.activeNetwork && persisted.activeNetwork in this.#networkUrls ? persisted.activeNetwork : config.activeNetwork ?? Object.keys(this.#networkUrls)[0] ?? "";
102
+ this.#activeAccount = persisted?.activeAccount ?? null;
57
103
  this.#name = config.name ?? DEFAULT_WALLET_NAME;
58
104
  this.#icon = config.icon ?? "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2NCA2NCIgd2lkdGg9IjY0IiBoZWlnaHQ9IjY0Ij4KICA8ZGVmcz4KICAgIDxsaW5lYXJHcmFkaWVudCBpZD0id2FsbGV0R3JhZCIgeDE9IjAlIiB5MT0iMCUiIHgyPSIxMDAlIiB5Mj0iMTAwJSI+CiAgICAgIDxzdG9wIG9mZnNldD0iMCUiIHN0eWxlPSJzdG9wLWNvbG9yOiM2MzY2RjEiIC8+CiAgICAgIDxzdG9wIG9mZnNldD0iMTAwJSIgc3R5bGU9InN0b3AtY29sb3I6IzI1NjNFQiIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQgaWQ9ImZsYXBHcmFkIiB4MT0iMCUiIHkxPSIwJSIgeDI9IjEwMCUiIHkyPSIxMDAlIj4KICAgICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3R5bGU9InN0b3AtY29sb3I6IzgxOENGOCIgLz4KICAgICAgPHN0b3Agb2Zmc2V0PSIxMDAlIiBzdHlsZT0ic3RvcC1jb2xvcjojNEY0NkU1IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICA8L2RlZnM+CiAgPCEtLSBTaGFkb3cgLS0+CiAgPHJlY3QgeD0iOSIgeT0iMjEiIHdpZHRoPSI1MCIgaGVpZ2h0PSIzNiIgcng9IjYiIHJ5PSI2IiBmaWxsPSIjMDAwIiBvcGFjaXR5PSIwLjEiIC8+CiAgPCEtLSBXYWxsZXQgYm9keSAtLT4KICA8cmVjdCB4PSI2IiB5PSIxOCIgd2lkdGg9IjUwIiBoZWlnaHQ9IjM2IiByeD0iNiIgcnk9IjYiIGZpbGw9InVybCgjd2FsbGV0R3JhZCkiIC8+CiAgPCEtLSBGbGFwIC0tPgogIDxwYXRoIGQ9Ik02IDI2IEw2IDIyIEM2IDE1IDggMTIgMTQgMTIgTDQ2IDEyIEM1MiAxMiA1NiAxNSA1NiAyMiBMNTYgMjYgWiIgZmlsbD0idXJsKCNmbGFwR3JhZCkiIC8+CiAgPCEtLSBDbGFzcCAtLT4KICA8cmVjdCB4PSI0MiIgeT0iMzAiIHdpZHRoPSIxOCIgaGVpZ2h0PSIxMiIgcng9IjQiIHJ5PSI0IiBmaWxsPSIjNDMzOENBIiAvPgogIDwhLS0gR29sZCBjb2luIGluIGNsYXNwIC0tPgogIDxjaXJjbGUgY3g9IjQ4IiBjeT0iMzYiIHI9IjMuNSIgZmlsbD0iI0ZDRDM0RCIgLz4KICA8Y2lyY2xlIGN4PSI0OCIgY3k9IjM2IiByPSIyIiBmaWxsPSIjRjU5RTBCIiAvPgogIDwhLS0gRW1ib3NzZWQgRFcgdGV4dCAtLT4KICA8dGV4dCB4PSIyNiIgeT0iNDUiIGZvbnQtZmFtaWx5PSJBcmlhbCwgSGVsdmV0aWNhLCBzYW5zLXNlcmlmIiBmb250LXdlaWdodD0iOTAwIiBmb250LXNpemU9IjE4IiBmaWxsPSIjMzczMEEzIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBkb21pbmFudC1iYXNlbGluZT0ibWlkZGxlIiBsZXR0ZXItc3BhY2luZz0iMiI+RFc8L3RleHQ+CiAgPHRleHQgeD0iMjYiIHk9IjQ0IiBmb250LWZhbWlseT0iQXJpYWwsIEhlbHZldGljYSwgc2Fucy1zZXJpZiIgZm9udC13ZWlnaHQ9IjkwMCIgZm9udC1zaXplPSIxOCIgZmlsbD0iI0M3RDJGRSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZG9taW5hbnQtYmFzZWxpbmU9Im1pZGxlIiBsZXR0ZXItc3BhY2luZz0iMiI+RFc8L3RleHQ+Cjwvc3ZnPgo=";
59
105
  this.#autoApprove = config.autoApprove ?? false;
@@ -201,20 +247,31 @@ var DevWallet = class {
201
247
  setActiveNetwork(network) {
202
248
  if (!this.#networkUrls[network]) throw new Error(`No client for network "${network}". Available: ${this.availableNetworks.join(", ")}`);
203
249
  this.#activeNetwork = network;
250
+ this.#saveState();
204
251
  this.#events.emit("change", { accounts: this.#exposedAccounts() });
205
252
  }
253
+ /** Address the wallet UI shows as active, or `null` when unset. Persisted
254
+ * with `persistState`. Unlike {@link setSelectedAccount}, this doesn't
255
+ * change which accounts dApps see. */
256
+ get activeAccount() {
257
+ return this.#activeAccount;
258
+ }
259
+ setActiveAccount(address) {
260
+ this.#activeAccount = address;
261
+ this.#saveState();
262
+ }
263
+ /**
264
+ * Add a network or replace its URL. `faucet` sets the network's faucet
265
+ * endpoint; pass `null` to remove it, or omit it to leave it unchanged.
266
+ */
206
267
  addNetwork(name, url, faucet) {
207
- let parsed;
208
- try {
209
- parsed = new URL(url);
210
- } catch {
211
- throw new Error(`Invalid URL: ${url}`);
212
- }
213
- if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw new Error(`Invalid URL: must use http or https (got ${parsed.protocol})`);
268
+ assertHttpUrl(url, `network "${name}"`);
269
+ if (faucet) assertHttpUrl(faucet, `the faucet of network "${name}"`);
214
270
  this.#networkUrls[name] = url;
215
271
  this.#clients[name] = this.#clientFactory(name, url);
216
- if (faucet !== void 0 && faucet !== null) this.#faucetUrls[name] = faucet;
217
- this.#saveNetworkUrls();
272
+ if (faucet) this.#faucetUrls[name] = faucet;
273
+ else if (faucet === null) delete this.#faucetUrls[name];
274
+ this.#saveState();
218
275
  this.#events.emit("change", { accounts: this.#exposedAccounts() });
219
276
  }
220
277
  removeNetwork(name) {
@@ -222,7 +279,7 @@ var DevWallet = class {
222
279
  delete this.#faucetUrls[name];
223
280
  delete this.#clients[name];
224
281
  if (this.#activeNetwork === name) this.#activeNetwork = Object.keys(this.#networkUrls)[0] ?? "";
225
- this.#saveNetworkUrls();
282
+ this.#saveState();
226
283
  this.#events.emit("change", { accounts: this.#exposedAccounts() });
227
284
  }
228
285
  /** Register this wallet with the wallet-standard registry. Returns an unregister function. */
@@ -367,20 +424,27 @@ var DevWallet = class {
367
424
  if (!network) throw new Error(`Invalid chain identifier: ${chain}`);
368
425
  return this.#ensureClient(network);
369
426
  }
370
- #loadNetworkUrls(defaults) {
371
- if (!this.#persistNetworks || typeof localStorage === "undefined") return { ...defaults };
372
- try {
373
- const raw = localStorage.getItem(NETWORK_STORAGE_KEY);
374
- if (raw) {
375
- const parsed = JSON.parse(raw);
376
- if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) return parsed;
377
- }
378
- } catch {}
379
- return { ...defaults };
380
- }
381
- #saveNetworkUrls() {
382
- if (!this.#persistNetworks || typeof localStorage === "undefined") return;
383
- localStorage.setItem(NETWORK_STORAGE_KEY, JSON.stringify(this.#networkUrls));
427
+ #loadState() {
428
+ if (!this.#persistState || typeof localStorage === "undefined") return null;
429
+ return readPersisted(localStorage, STATE_STORAGE_KEY, decodePersistedState);
430
+ }
431
+ /** Current faucets plus an empty-string marker for each config faucet the
432
+ * user removed, so the removal survives a reload. */
433
+ #persistableFaucets() {
434
+ const faucets = { ...this.#faucetUrls };
435
+ for (const name of Object.keys(this.#configFaucets)) if (!(name in faucets)) faucets[name] = "";
436
+ return faucets;
437
+ }
438
+ #saveState() {
439
+ if (!this.#persistState || typeof localStorage === "undefined") return;
440
+ const state = {
441
+ version: 1,
442
+ networks: this.#networkUrls,
443
+ faucets: this.#persistableFaucets(),
444
+ activeNetwork: this.#activeNetwork,
445
+ ...this.#activeAccount ? { activeAccount: this.#activeAccount } : {}
446
+ };
447
+ localStorage.setItem(STATE_STORAGE_KEY, JSON.stringify(state));
384
448
  }
385
449
  #ensureClient(network) {
386
450
  if (!this.#clients[network]) {
@@ -1 +1 @@
1
- {"version":3,"file":"dev-wallet.mjs","names":["#adapters","#name","#icon","#autoApprove","#autoConnect","#clientFactory","#persistNetworks","#networkUrls","#loadNetworkUrls","#faucetUrls","#clients","#activeNetwork","#accounts","#aggregateAccounts","#events","#unsubscribeAdapters","#pendingRequest","#requestListeners","#pendingConnect","#connectListeners","#connectedListeners","#setupAdapterListeners","#exposedAccounts","#selectedAddress","#connect","#on","#disconnect","#signPersonalMessage","#signTransaction","#signAndExecuteTransaction","#ensureClient","#saveNetworkUrls","#destroyed","#executeSigning","#notifyRequestListeners","#notifyConnectListeners","#notifyConnectedListeners","#getSigner","#getClientForChain","#shouldAutoApprove","#enqueueRequest"],"sources":["../../src/wallet/dev-wallet.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { ClientWithCoreApi } from '@mysten/sui/client';\nimport { SuiGrpcClient } from '@mysten/sui/grpc';\nimport { mitt, type Emitter } from '@mysten/utils';\nimport type {\n\tStandardConnectFeature,\n\tStandardConnectMethod,\n\tStandardDisconnectFeature,\n\tStandardDisconnectMethod,\n\tStandardEventsFeature,\n\tStandardEventsOnMethod,\n\tSuiFeatures,\n\tSuiSignAndExecuteTransactionMethod,\n\tSuiSignPersonalMessageMethod,\n\tSuiSignTransactionMethod,\n\tWallet,\n\tWalletAccount,\n\tWalletIcon,\n} from '@mysten/wallet-standard';\nimport { getWallets, ReadonlyWalletAccount } from '@mysten/wallet-standard';\n\nimport { chainsForNetworks } from '../adapters/build-managed-account.js';\nimport type { SignerAdapter } from '../types.js';\nimport { DEFAULT_WALLET_ICON, getNetworkFromChain, type WalletEventsMap } from './constants.js';\nimport { type SigningResult, executeSigning } from './signing.js';\n\nconst DEFAULT_WALLET_NAME = 'Dev Wallet';\nconst NETWORK_STORAGE_KEY = 'dev-wallet:networks';\n\n/**\n * Default gRPC endpoint URLs for standard Sui networks (devnet, testnet, localnet).\n */\nexport const DEFAULT_NETWORK_URLS = {\n\tdevnet: 'https://fullnode.devnet.sui.io:443',\n\ttestnet: 'https://fullnode.testnet.sui.io:443',\n\tlocalnet: 'http://127.0.0.1:9000',\n} as const;\n\n/** A pending signing request waiting for user approval via the wallet UI. */\nexport interface WalletRequest {\n\tid: string;\n\ttype: 'sign-transaction' | 'sign-and-execute-transaction' | 'sign-personal-message';\n\taccount: WalletAccount;\n\tchain: string;\n\t/** Transaction JSON string for sign/execute, Uint8Array for personal messages. */\n\tdata: string | Uint8Array;\n\tresolve: (result: SigningResult) => void;\n\treject: (error: Error) => void;\n}\n\n/** Public view of a pending signing request, omitting internal resolve/reject callbacks. */\nexport type PendingSigningRequest = Omit<WalletRequest, 'resolve' | 'reject'>;\n\nexport interface ConnectRequest {\n\tid: string;\n\tresolve: (result: { accounts: readonly WalletAccount[] }) => void;\n\treject: (error: Error) => void;\n}\n\n/** Public view of a pending connect request, omitting internal resolve/reject callbacks. */\nexport type PendingConnectRequest = Omit<ConnectRequest, 'resolve' | 'reject'>;\n\n/**\n * Auto-approval policy for signing requests.\n * `true` approves all requests; a function is called per-request and returns `true` to approve.\n */\nexport type AutoApprovePolicy =\n\t| boolean\n\t| ((request: {\n\t\t\ttype: WalletRequest['type'];\n\t\t\taccount: WalletAccount;\n\t\t\tchain: string;\n\t\t\tdata: string | Uint8Array;\n\t }) => boolean);\n\nexport interface DevWalletConfig {\n\tadapters: SignerAdapter[];\n\t/** Network URLs by name (network → gRPC endpoint). Defaults to devnet, testnet, and localnet. */\n\tnetworks?: Record<string, string>;\n\t/** Optional per-network faucet endpoints (network → faucet URL). The\n\t * active network's faucet is resolvable via {@link DevWallet.activeFaucet}\n\t * / {@link DevWallet.getFaucet}, so a fund flow funds the SELECTED\n\t * network's account. Networks without a faucet (live mainnet, fork\n\t * stacks) simply omit a key. */\n\tfaucets?: Record<string, string>;\n\t/** Display name for the wallet. Defaults to 'Dev Wallet'. */\n\tname?: string;\n\ticon?: WalletIcon;\n\t/** Initially active network. Defaults to the first key in `networks`. */\n\tactiveNetwork?: string;\n\t/**\n\t * When set, matching requests are signed immediately without queuing for user approval.\n\t * `true` approves everything; a function is called per-request for fine-grained control.\n\t */\n\tautoApprove?: AutoApprovePolicy;\n\t/** When true, connect requests resolve immediately with all accounts. */\n\tautoConnect?: boolean;\n\t/** Factory to create a client for a given network. Defaults to SuiGrpcClient. */\n\tclientFactory?: (network: string, url: string) => ClientWithCoreApi;\n\t/**\n\t * Persist network URLs to localStorage so custom configurations survive page reloads\n\t * and are shared with popup windows.\n\t */\n\tpersistNetworks?: boolean;\n}\n\n/**\n * wallet-standard Wallet implementation for development. All signing requests are queued\n * and require explicit approval — either via the UI or programmatically via `approveRequest()`.\n */\nexport class DevWallet implements Wallet {\n\treadonly #adapters: SignerAdapter[];\n\t#networkUrls: Record<string, string>;\n\t#faucetUrls: Record<string, string>;\n\t#clients: Record<string, ClientWithCoreApi>;\n\t#activeNetwork: string;\n\treadonly #name: string;\n\treadonly #icon: WalletIcon;\n\treadonly #autoApprove: AutoApprovePolicy;\n\treadonly #autoConnect: boolean;\n\treadonly #clientFactory: (network: string, url: string) => ClientWithCoreApi;\n\treadonly #persistNetworks: boolean;\n\t#accounts: ReadonlyWalletAccount[];\n\t#selectedAddress: string | null = null;\n\t#events: Emitter<WalletEventsMap>;\n\t#unsubscribeAdapters: (() => void)[];\n\t#pendingRequest: WalletRequest | null;\n\t#requestListeners: Set<(request: PendingSigningRequest | null) => void>;\n\t#pendingConnect: ConnectRequest | null;\n\t#connectListeners: Set<(request: PendingConnectRequest | null) => void>;\n\t/** Fired every time a dApp invokes `standard:connect` (the wallet's\n\t * authoritative \"a dApp is connecting\" signal). Distinct from\n\t * `#connectListeners`, which track the *pending-request* lifecycle for the\n\t * approval UI. Consumers (e.g. the inject's `selectAccount`) use this to\n\t * observe that the dApp has reached the wallet rather than racing a timer. */\n\t#connectedListeners: Set<() => void>;\n\t#destroyed = false;\n\n\tconstructor(config: DevWalletConfig) {\n\t\tif (config.adapters.length === 0) {\n\t\t\tconsole.warn('[dev-wallet] No adapters provided. The wallet will have no accounts.');\n\t\t}\n\t\tthis.#adapters = config.adapters;\n\t\tthis.#persistNetworks = config.persistNetworks ?? false;\n\t\tthis.#networkUrls = this.#loadNetworkUrls(config.networks ?? DEFAULT_NETWORK_URLS);\n\t\tthis.#faucetUrls = { ...config.faucets };\n\t\tthis.#clients = {};\n\t\tthis.#activeNetwork = config.activeNetwork ?? Object.keys(this.#networkUrls)[0] ?? '';\n\t\tthis.#name = config.name ?? DEFAULT_WALLET_NAME;\n\t\tthis.#icon = config.icon ?? DEFAULT_WALLET_ICON;\n\t\tthis.#autoApprove = config.autoApprove ?? false;\n\t\tthis.#autoConnect = config.autoConnect ?? false;\n\t\tthis.#clientFactory =\n\t\t\tconfig.clientFactory ?? ((network, url) => new SuiGrpcClient({ baseUrl: url, network }));\n\t\tthis.#accounts = this.#aggregateAccounts();\n\t\tthis.#events = mitt();\n\t\tthis.#unsubscribeAdapters = [];\n\t\tthis.#pendingRequest = null;\n\t\tthis.#requestListeners = new Set();\n\t\tthis.#pendingConnect = null;\n\t\tthis.#connectListeners = new Set();\n\t\tthis.#connectedListeners = new Set();\n\n\t\tthis.#setupAdapterListeners();\n\t}\n\n\tget version() {\n\t\treturn '1.0.0' as const;\n\t}\n\n\tget name() {\n\t\treturn this.#name;\n\t}\n\n\tget icon() {\n\t\treturn this.#icon;\n\t}\n\n\t/**\n\t * Wallet-standard chains this wallet advertises. Reflects the CONFIGURED\n\t * networks — each network name is advertised as `sui:<name>` so dApp Kit's\n\t * chain-gated paths work for fork networks (`testnet-fork`, …) and custom\n\t * network names, not just the fixed standard set. Unioned with the standard\n\t * Sui chains for safety, de-duplicated, with the standard chains first and\n\t * configured-only chains appended in network-map order.\n\t */\n\tget chains(): `sui:${string}`[] {\n\t\treturn chainsForNetworks(Object.keys(this.#networkUrls));\n\t}\n\n\tget accounts(): readonly ReadonlyWalletAccount[] {\n\t\treturn this.#exposedAccounts();\n\t}\n\n\t/**\n\t * Narrow the accounts this wallet exposes to dApps (via the\n\t * wallet-standard `accounts` array + `change` events) to a single\n\t * address, or pass `null` to expose all aggregated accounts again.\n\t *\n\t * Used by the devstack page-register entry to drive headless account\n\t * selection: dApp Kit reflects the `change` event by switching its\n\t * connected account to the now-single exposed account (or, if not yet\n\t * connected, auto-connects to it). No reference to the app's dApp Kit\n\t * instance is required — selection rides the wallet-standard protocol.\n\t */\n\tsetSelectedAccount(address: string | null): void {\n\t\tif (address !== null) {\n\t\t\tconst match = this.#accounts.find((a) => a.address.toLowerCase() === address.toLowerCase());\n\t\t\tif (match === undefined) {\n\t\t\t\tconst available = this.#accounts.map((a) => a.address).join(', ');\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`No dev-wallet account for address \"${address}\". Available: ${available || '(none)'}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.#selectedAddress = match.address;\n\t\t} else {\n\t\t\tthis.#selectedAddress = null;\n\t\t}\n\t\tthis.#events.emit('change', { accounts: this.#exposedAccounts() });\n\t}\n\n\t/** Accounts exposed to dApps — narrowed to the selected address when set. */\n\t#exposedAccounts(): ReadonlyWalletAccount[] {\n\t\tif (this.#selectedAddress === null) return this.#accounts;\n\t\treturn this.#accounts.filter(\n\t\t\t(a) => a.address.toLowerCase() === this.#selectedAddress!.toLowerCase(),\n\t\t);\n\t}\n\n\tget features(): StandardConnectFeature &\n\t\tStandardEventsFeature &\n\t\tStandardDisconnectFeature &\n\t\tSuiFeatures {\n\t\treturn {\n\t\t\t'standard:connect': {\n\t\t\t\tversion: '1.0.0',\n\t\t\t\tconnect: this.#connect,\n\t\t\t},\n\t\t\t'standard:events': {\n\t\t\t\tversion: '1.0.0',\n\t\t\t\ton: this.#on,\n\t\t\t},\n\t\t\t'standard:disconnect': {\n\t\t\t\tversion: '1.0.0',\n\t\t\t\tdisconnect: this.#disconnect,\n\t\t\t},\n\t\t\t'sui:signPersonalMessage': {\n\t\t\t\tversion: '1.1.0',\n\t\t\t\tsignPersonalMessage: this.#signPersonalMessage,\n\t\t\t},\n\t\t\t'sui:signTransaction': {\n\t\t\t\tversion: '2.0.0',\n\t\t\t\tsignTransaction: this.#signTransaction,\n\t\t\t},\n\t\t\t'sui:signAndExecuteTransaction': {\n\t\t\t\tversion: '2.0.0',\n\t\t\t\tsignAndExecuteTransaction: this.#signAndExecuteTransaction,\n\t\t\t},\n\t\t};\n\t}\n\n\t/** The current pending signing request, or null if none. */\n\tget pendingRequest(): PendingSigningRequest | null {\n\t\treturn this.#pendingRequest;\n\t}\n\n\tget adapters(): readonly SignerAdapter[] {\n\t\treturn this.#adapters;\n\t}\n\n\tgetAdapterForAccount(address: string): SignerAdapter | undefined {\n\t\treturn this.#adapters.find((a) => a.getAccount(address) !== undefined);\n\t}\n\n\t/** Eagerly creates and returns clients for all configured networks. Prefer `getClient()` or `activeClient` for single-network access. */\n\tget clients(): Record<string, ClientWithCoreApi> {\n\t\tfor (const name of Object.keys(this.#networkUrls)) {\n\t\t\tthis.#ensureClient(name);\n\t\t}\n\t\treturn { ...this.#clients };\n\t}\n\n\tgetClient(network: string): ClientWithCoreApi {\n\t\treturn this.#ensureClient(network);\n\t}\n\n\tget networkUrls(): Record<string, string> {\n\t\treturn { ...this.#networkUrls };\n\t}\n\n\t/** Per-network faucet endpoints (network → faucet URL). Networks without\n\t * a faucet are absent from the map. */\n\tget faucetUrls(): Record<string, string> {\n\t\treturn { ...this.#faucetUrls };\n\t}\n\n\t/** Faucet endpoint for the named network, or `null` when that network has\n\t * no faucet configured. */\n\tgetFaucet(network: string): string | null {\n\t\treturn this.#faucetUrls[network] ?? null;\n\t}\n\n\t/** Faucet endpoint for the currently-active network, or `null` when the\n\t * active network has no faucet (live mainnet, fork stacks). Drives a\n\t * fund flow against the SELECTED network. */\n\tget activeFaucet(): string | null {\n\t\treturn this.getFaucet(this.#activeNetwork);\n\t}\n\n\tget activeNetwork(): string {\n\t\treturn this.#activeNetwork;\n\t}\n\n\tget activeClient(): ClientWithCoreApi | null {\n\t\tif (!this.#activeNetwork || !this.#networkUrls[this.#activeNetwork]) return null;\n\t\treturn this.#ensureClient(this.#activeNetwork);\n\t}\n\n\tget availableNetworks(): string[] {\n\t\treturn Object.keys(this.#networkUrls);\n\t}\n\n\t/** Switch the active network. Emits a `change` event. */\n\tsetActiveNetwork(network: string): void {\n\t\tif (!this.#networkUrls[network]) {\n\t\t\tthrow new Error(\n\t\t\t\t`No client for network \"${network}\". Available: ${this.availableNetworks.join(', ')}`,\n\t\t\t);\n\t\t}\n\t\tthis.#activeNetwork = network;\n\t\tthis.#events.emit('change', { accounts: this.#exposedAccounts() });\n\t}\n\n\taddNetwork(name: string, url: string, faucet?: string | null): void {\n\t\tlet parsed: URL;\n\t\ttry {\n\t\t\tparsed = new URL(url);\n\t\t} catch {\n\t\t\tthrow new Error(`Invalid URL: ${url}`);\n\t\t}\n\t\tif (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n\t\t\tthrow new Error(`Invalid URL: must use http or https (got ${parsed.protocol})`);\n\t\t}\n\t\tthis.#networkUrls[name] = url;\n\t\tthis.#clients[name] = this.#clientFactory(name, url);\n\t\tif (faucet !== undefined && faucet !== null) {\n\t\t\tthis.#faucetUrls[name] = faucet;\n\t\t}\n\t\tthis.#saveNetworkUrls();\n\t\tthis.#events.emit('change', { accounts: this.#exposedAccounts() });\n\t}\n\n\tremoveNetwork(name: string): void {\n\t\tdelete this.#networkUrls[name];\n\t\tdelete this.#faucetUrls[name];\n\t\tdelete this.#clients[name];\n\t\tif (this.#activeNetwork === name) {\n\t\t\tthis.#activeNetwork = Object.keys(this.#networkUrls)[0] ?? '';\n\t\t}\n\t\tthis.#saveNetworkUrls();\n\t\tthis.#events.emit('change', { accounts: this.#exposedAccounts() });\n\t}\n\n\t/** Register this wallet with the wallet-standard registry. Returns an unregister function. */\n\tregister(): () => void {\n\t\tif (typeof process !== 'undefined' && process.env?.NODE_ENV === 'production') {\n\t\t\tconsole.warn(\n\t\t\t\t'[@mysten-incubation/dev-wallet] DevWallet is intended for development only. Do not use in production.',\n\t\t\t);\n\t\t}\n\t\tconst walletsApi = getWallets();\n\t\treturn walletsApi.register(this);\n\t}\n\n\t/** Reject any pending requests, unsubscribe from adapters, destroy them, and clear all listeners. */\n\tdestroy(): void {\n\t\tthis.#destroyed = true;\n\t\tfor (const unsub of this.#unsubscribeAdapters) {\n\t\t\tunsub();\n\t\t}\n\t\tthis.#unsubscribeAdapters = [];\n\t\tfor (const adapter of this.#adapters) {\n\t\t\tadapter.destroy();\n\t\t}\n\t\tif (this.#pendingRequest) {\n\t\t\tthis.#pendingRequest.reject(new Error('Wallet destroyed.'));\n\t\t\tthis.#pendingRequest = null;\n\t\t}\n\t\tif (this.#pendingConnect) {\n\t\t\tthis.#pendingConnect.reject(new Error('Wallet destroyed.'));\n\t\t\tthis.#pendingConnect = null;\n\t\t}\n\t\tthis.#requestListeners.clear();\n\t\tthis.#connectListeners.clear();\n\t\tthis.#connectedListeners.clear();\n\t\tthis.#events.all.clear();\n\t}\n\n\t/** Sign the pending request using the account's adapter signer and resolve the promise. */\n\tasync approveRequest(): Promise<void> {\n\t\tconst request = this.#pendingRequest;\n\t\tif (!request) {\n\t\t\tthrow new Error('No pending request to approve.');\n\t\t}\n\n\t\ttry {\n\t\t\tconst result = await this.#executeSigning(\n\t\t\t\trequest.type,\n\t\t\t\trequest.account,\n\t\t\t\trequest.chain,\n\t\t\t\trequest.data,\n\t\t\t);\n\t\t\tthis.#pendingRequest = null;\n\t\t\tthis.#notifyRequestListeners();\n\t\t\trequest.resolve(result);\n\t\t} catch (error) {\n\t\t\tthis.#pendingRequest = null;\n\t\t\tthis.#notifyRequestListeners();\n\t\t\trequest.reject(error instanceof Error ? error : new Error(String(error)));\n\t\t}\n\t}\n\n\trejectRequest(reason?: string): void {\n\t\tconst request = this.#pendingRequest;\n\t\tif (!request) {\n\t\t\tthrow new Error('No pending request to reject.');\n\t\t}\n\t\tthis.#pendingRequest = null;\n\t\tthis.#notifyRequestListeners();\n\t\trequest.reject(new Error(reason ?? 'Request rejected by user.'));\n\t}\n\n\t/** Subscribe to pending request changes. Returns an unsubscribe function. */\n\tonRequestChange(callback: (request: PendingSigningRequest | null) => void): () => void {\n\t\tthis.#requestListeners.add(callback);\n\t\treturn () => {\n\t\t\tthis.#requestListeners.delete(callback);\n\t\t};\n\t}\n\n\t/** The current pending connect request, or null if none. */\n\tget pendingConnect(): PendingConnectRequest | null {\n\t\treturn this.#pendingConnect;\n\t}\n\n\t/** Expose the given addresses to the dApp. Pass an empty array to expose all accounts. */\n\tapproveConnect(selectedAddresses: string[]): void {\n\t\tconst request = this.#pendingConnect;\n\t\tif (!request) {\n\t\t\tthrow new Error('No pending connect request to approve.');\n\t\t}\n\n\t\tconst selected =\n\t\t\tselectedAddresses.length > 0\n\t\t\t\t? this.#accounts.filter((a) => selectedAddresses.includes(a.address))\n\t\t\t\t: [...this.#accounts];\n\n\t\tthis.#pendingConnect = null;\n\t\tthis.#notifyConnectListeners();\n\t\trequest.resolve({ accounts: selected });\n\t}\n\n\trejectConnect(reason?: string): void {\n\t\tconst request = this.#pendingConnect;\n\t\tif (!request) {\n\t\t\tthrow new Error('No pending connect request to reject.');\n\t\t}\n\t\tthis.#pendingConnect = null;\n\t\tthis.#notifyConnectListeners();\n\t\trequest.reject(new Error(reason ?? 'Connection rejected by user.'));\n\t}\n\n\t/** Subscribe to pending connect request changes. Returns an unsubscribe function. */\n\tonConnectChange(callback: (request: PendingConnectRequest | null) => void): () => void {\n\t\tthis.#connectListeners.add(callback);\n\t\treturn () => {\n\t\t\tthis.#connectListeners.delete(callback);\n\t\t};\n\t}\n\n\t#notifyRequestListeners() {\n\t\tconst request = this.#pendingRequest;\n\t\tfor (const listener of this.#requestListeners) {\n\t\t\tlistener(request);\n\t\t}\n\t}\n\n\t#notifyConnectListeners() {\n\t\tconst request = this.#pendingConnect;\n\t\tfor (const listener of this.#connectListeners) {\n\t\t\tlistener(request);\n\t\t}\n\t}\n\n\t/**\n\t * Subscribe to the \"a dApp invoked `standard:connect`\" edge. Returns an\n\t * unsubscribe function. Used by the page-register entry to gate its\n\t * narrow→widen account-selection on an OBSERVED connect rather than a fixed\n\t * timer (see `src/inject/index.ts`).\n\t */\n\tonDAppConnected(callback: () => void): () => void {\n\t\tthis.#connectedListeners.add(callback);\n\t\treturn () => {\n\t\t\tthis.#connectedListeners.delete(callback);\n\t\t};\n\t}\n\n\t#notifyConnectedListeners() {\n\t\tfor (const listener of this.#connectedListeners) {\n\t\t\ttry {\n\t\t\t\tlistener();\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('[dev-wallet] onDAppConnected listener threw:', error);\n\t\t\t}\n\t\t}\n\t}\n\n\t#aggregateAccounts(): ReadonlyWalletAccount[] {\n\t\treturn this.#adapters.flatMap((a) => a.getAccounts().map((acc) => acc.walletAccount));\n\t}\n\n\t#setupAdapterListeners() {\n\t\tfor (const adapter of this.#adapters) {\n\t\t\tconst unsub = adapter.onAccountsChanged(() => {\n\t\t\t\tthis.#accounts = this.#aggregateAccounts();\n\t\t\t\tthis.#events.emit('change', { accounts: this.#exposedAccounts() });\n\t\t\t});\n\t\t\tthis.#unsubscribeAdapters.push(unsub);\n\t\t}\n\t}\n\n\t#on: StandardEventsOnMethod = (event, listener) => {\n\t\tthis.#events.on(event, listener);\n\t\treturn () => this.#events.off(event, listener);\n\t};\n\n\t#connect: StandardConnectMethod = async () => {\n\t\t// Notify observers that a dApp has invoked `standard:connect`. This is\n\t\t// the wallet's authoritative \"a dApp reached us\" edge — fired before we\n\t\t// branch on auto/queued so it covers both the silent auto-connect path\n\t\t// and an interactive connect.\n\t\tthis.#notifyConnectedListeners();\n\n\t\t// Auto-connect: return all accounts immediately\n\t\tif (this.#autoConnect) {\n\t\t\treturn { accounts: this.accounts };\n\t\t}\n\n\t\t// Queue a connect request for user to select accounts\n\t\tif (this.#pendingConnect) {\n\t\t\treturn Promise.reject(new Error('A connect request is already pending.'));\n\t\t}\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tthis.#pendingConnect = {\n\t\t\t\tid: crypto.randomUUID(),\n\t\t\t\tresolve,\n\t\t\t\treject,\n\t\t\t};\n\t\t\tthis.#notifyConnectListeners();\n\t\t});\n\t};\n\n\t#disconnect: StandardDisconnectMethod = async () => {\n\t\t// Disconnecting ends the dApp session but does not affect wallet state.\n\t\t// The wallet retains its accounts and adapter subscriptions.\n\t};\n\n\t#getClientForChain(chain: string) {\n\t\tconst network = getNetworkFromChain(chain);\n\t\tif (!network) {\n\t\t\tthrow new Error(`Invalid chain identifier: ${chain}`);\n\t\t}\n\t\treturn this.#ensureClient(network);\n\t}\n\n\t#loadNetworkUrls(defaults: Record<string, string>): Record<string, string> {\n\t\tif (!this.#persistNetworks || typeof localStorage === 'undefined') {\n\t\t\treturn { ...defaults };\n\t\t}\n\t\ttry {\n\t\t\tconst raw = localStorage.getItem(NETWORK_STORAGE_KEY);\n\t\t\tif (raw) {\n\t\t\t\tconst parsed = JSON.parse(raw);\n\t\t\t\tif (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {\n\t\t\t\t\treturn parsed as Record<string, string>;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// Corrupted data — fall through to defaults\n\t\t}\n\t\treturn { ...defaults };\n\t}\n\n\t#saveNetworkUrls(): void {\n\t\tif (!this.#persistNetworks || typeof localStorage === 'undefined') return;\n\t\tlocalStorage.setItem(NETWORK_STORAGE_KEY, JSON.stringify(this.#networkUrls));\n\t}\n\n\t#ensureClient(network: string): ClientWithCoreApi {\n\t\tif (!this.#clients[network]) {\n\t\t\tconst url = this.#networkUrls[network];\n\t\t\tif (!url) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`No client for network \"${network}\". Available: ${this.availableNetworks.join(', ')}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.#clients[network] = this.#clientFactory(network, url);\n\t\t}\n\t\treturn this.#clients[network];\n\t}\n\n\t#getSigner(address: string) {\n\t\tfor (const adapter of this.#adapters) {\n\t\t\tconst account = adapter.getAccount(address);\n\t\t\tif (account) return account.signer;\n\t\t}\n\t\tconst allAddresses = this.#adapters.flatMap((a) => a.getAccounts().map((acc) => acc.address));\n\t\tthrow new Error(\n\t\t\t`No account found for address \"${address}\". Available addresses: ${allAddresses.join(', ')}`,\n\t\t);\n\t}\n\n\t#shouldAutoApprove(\n\t\ttype: WalletRequest['type'],\n\t\taccount: WalletAccount,\n\t\tchain: string,\n\t\tdata: string | Uint8Array,\n\t): boolean {\n\t\t// Check adapter-level opt-out first — CLI adapters never auto-sign\n\t\tconst adapter = this.getAdapterForAccount(account.address);\n\t\tif (adapter?.allowAutoSign === false) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (this.#autoApprove === true) {\n\t\t\treturn true;\n\t\t}\n\t\tif (typeof this.#autoApprove === 'function') {\n\t\t\treturn this.#autoApprove({ type, account, chain, data });\n\t\t}\n\t\treturn false;\n\t}\n\n\tasync #executeSigning(\n\t\ttype: WalletRequest['type'],\n\t\taccount: WalletAccount,\n\t\tchain: string,\n\t\tdata: string | Uint8Array,\n\t) {\n\t\tconst signer = this.#getSigner(account.address);\n\t\tconst client = type !== 'sign-personal-message' ? this.#getClientForChain(chain) : undefined;\n\t\treturn executeSigning({ type, signer, data, client });\n\t}\n\n\t#enqueueRequest<T extends WalletRequest['type']>(\n\t\ttype: T,\n\t\taccount: WalletAccount,\n\t\tchain: string,\n\t\tdata: string | Uint8Array,\n\t): Promise<Extract<SigningResult, { type: T }>> {\n\t\ttype Result = Extract<SigningResult, { type: T }>;\n\t\tif (this.#destroyed) {\n\t\t\treturn Promise.reject(new Error('Wallet has been destroyed.'));\n\t\t}\n\t\tif (this.#shouldAutoApprove(type, account, chain, data)) {\n\t\t\treturn this.#executeSigning(type, account, chain, data) as Promise<Result>;\n\t\t}\n\n\t\t// Only one request at a time — subsequent requests are immediately rejected.\n\t\t// DApps that batch transactions should serialize their signing calls.\n\t\tif (this.#pendingRequest) {\n\t\t\treturn Promise.reject(new Error('A signing request is already pending.'));\n\t\t}\n\n\t\treturn new Promise<Result>((resolve, reject) => {\n\t\t\tthis.#pendingRequest = {\n\t\t\t\tid: crypto.randomUUID(),\n\t\t\t\ttype,\n\t\t\t\taccount,\n\t\t\t\tchain,\n\t\t\t\tdata,\n\t\t\t\tresolve: resolve as (result: SigningResult) => void,\n\t\t\t\treject,\n\t\t\t};\n\t\t\tthis.#notifyRequestListeners();\n\t\t});\n\t}\n\n\t#signPersonalMessage: SuiSignPersonalMessageMethod = async (input) => {\n\t\treturn this.#enqueueRequest(\n\t\t\t'sign-personal-message',\n\t\t\tinput.account,\n\t\t\tinput.chain ?? 'sui:unknown',\n\t\t\tinput.message,\n\t\t);\n\t};\n\n\t#signTransaction: SuiSignTransactionMethod = async (input) => {\n\t\tconst txJson = await input.transaction.toJSON();\n\t\treturn this.#enqueueRequest('sign-transaction', input.account, input.chain, txJson);\n\t};\n\n\t#signAndExecuteTransaction: SuiSignAndExecuteTransactionMethod = async (input) => {\n\t\tconst txJson = await input.transaction.toJSON();\n\t\treturn this.#enqueueRequest('sign-and-execute-transaction', input.account, input.chain, txJson);\n\t};\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"dev-wallet.mjs","names":["#adapters","#configFaucets","#name","#icon","#autoApprove","#autoConnect","#clientFactory","#persistState","#loadState","#networkUrls","#faucetUrls","#clients","#activeNetwork","#activeAccount","#accounts","#aggregateAccounts","#events","#unsubscribeAdapters","#pendingRequest","#requestListeners","#pendingConnect","#connectListeners","#connectedListeners","#setupAdapterListeners","#exposedAccounts","#selectedAddress","#connect","#on","#disconnect","#signPersonalMessage","#signTransaction","#signAndExecuteTransaction","#ensureClient","#saveState","#destroyed","#executeSigning","#notifyRequestListeners","#notifyConnectListeners","#notifyConnectedListeners","#persistableFaucets","#getSigner","#getClientForChain","#shouldAutoApprove","#enqueueRequest"],"sources":["../../src/wallet/dev-wallet.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { ClientWithCoreApi } from '@mysten/sui/client';\nimport { SuiGrpcClient } from '@mysten/sui/grpc';\nimport { mitt, type Emitter } from '@mysten/utils';\nimport type {\n\tStandardConnectFeature,\n\tStandardConnectMethod,\n\tStandardDisconnectFeature,\n\tStandardDisconnectMethod,\n\tStandardEventsFeature,\n\tStandardEventsOnMethod,\n\tSuiFeatures,\n\tSuiSignAndExecuteTransactionMethod,\n\tSuiSignPersonalMessageMethod,\n\tSuiSignTransactionMethod,\n\tWallet,\n\tWalletAccount,\n\tWalletIcon,\n} from '@mysten/wallet-standard';\nimport { getWallets, ReadonlyWalletAccount } from '@mysten/wallet-standard';\n\nimport { chainsForNetworks } from '../adapters/build-managed-account.js';\nimport type { SignerAdapter } from '../types.js';\nimport { DEFAULT_WALLET_ICON, getNetworkFromChain, type WalletEventsMap } from './constants.js';\nimport { readPersisted } from './persisted.js';\nimport { type SigningResult, executeSigning } from './signing.js';\n\nconst DEFAULT_WALLET_NAME = 'Dev Wallet';\n/** localStorage key for {@link PersistedStateV1}. Bump the suffix on shape changes. */\nexport const STATE_STORAGE_KEY = 'dev-wallet:state:v1';\n\n/** Wallet state persisted across reloads when `persistState` is enabled. */\ninterface PersistedStateV1 {\n\tversion: 1;\n\tnetworks: Record<string, string>;\n\tfaucets?: Record<string, string>;\n\tactiveNetwork?: string;\n\tactiveAccount?: string;\n}\n\nfunction isStringRecord(value: unknown): value is Record<string, string> {\n\treturn (\n\t\ttypeof value === 'object' &&\n\t\tvalue !== null &&\n\t\t!Array.isArray(value) &&\n\t\tObject.values(value).every((v) => typeof v === 'string')\n\t);\n}\n\n/** Throws unless `url` is an http(s) URL. `what` names it in the error, e.g. `network \"localnet\"`. */\nfunction assertHttpUrl(url: string, what: string): void {\n\tlet parsed: URL;\n\ttry {\n\t\tparsed = new URL(url);\n\t} catch {\n\t\tthrow new Error(`Invalid URL for ${what}: ${url}`);\n\t}\n\tif (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n\t\tthrow new Error(`Invalid URL for ${what}: must use http or https (got ${parsed.protocol})`);\n\t}\n}\n\n/** Config faucets overlaid with persisted ones; `''` marks a removed faucet. */\nfunction mergeFaucets(\n\tconfig: Record<string, string>,\n\tpersisted: Record<string, string> | undefined,\n): Record<string, string> {\n\tconst merged = { ...config, ...persisted };\n\tfor (const [name, url] of Object.entries(merged)) {\n\t\tif (url === '') delete merged[name];\n\t}\n\treturn merged;\n}\n\nfunction decodePersistedState(parsed: unknown): PersistedStateV1 | null {\n\tif (typeof parsed !== 'object' || parsed === null) return null;\n\tconst { version, networks, faucets, activeNetwork, activeAccount } = parsed as Record<\n\t\tstring,\n\t\tunknown\n\t>;\n\tif (version !== 1) return null;\n\tif (!isStringRecord(networks)) return null;\n\tif (faucets !== undefined && !isStringRecord(faucets)) return null;\n\tif (activeNetwork !== undefined && typeof activeNetwork !== 'string') return null;\n\tif (activeAccount !== undefined && typeof activeAccount !== 'string') return null;\n\treturn {\n\t\tversion,\n\t\tnetworks,\n\t\tfaucets,\n\t\tactiveNetwork,\n\t\tactiveAccount,\n\t};\n}\n\n/**\n * Default gRPC endpoint URLs for standard Sui networks (devnet, testnet, localnet).\n */\nexport const DEFAULT_NETWORK_URLS = {\n\tdevnet: 'https://fullnode.devnet.sui.io:443',\n\ttestnet: 'https://fullnode.testnet.sui.io:443',\n\tlocalnet: 'http://127.0.0.1:9000',\n} as const;\n\n/** A pending signing request waiting for user approval via the wallet UI. */\nexport interface WalletRequest {\n\tid: string;\n\ttype: 'sign-transaction' | 'sign-and-execute-transaction' | 'sign-personal-message';\n\taccount: WalletAccount;\n\tchain: string;\n\t/** Transaction JSON string for sign/execute, Uint8Array for personal messages. */\n\tdata: string | Uint8Array;\n\tresolve: (result: SigningResult) => void;\n\treject: (error: Error) => void;\n}\n\n/** Public view of a pending signing request, omitting internal resolve/reject callbacks. */\nexport type PendingSigningRequest = Omit<WalletRequest, 'resolve' | 'reject'>;\n\nexport interface ConnectRequest {\n\tid: string;\n\tresolve: (result: { accounts: readonly WalletAccount[] }) => void;\n\treject: (error: Error) => void;\n}\n\n/** Public view of a pending connect request, omitting internal resolve/reject callbacks. */\nexport type PendingConnectRequest = Omit<ConnectRequest, 'resolve' | 'reject'>;\n\n/**\n * Auto-approval policy for signing requests.\n * `true` approves all requests; a function is called per-request and returns `true` to approve.\n */\nexport type AutoApprovePolicy =\n\t| boolean\n\t| ((request: {\n\t\t\ttype: WalletRequest['type'];\n\t\t\taccount: WalletAccount;\n\t\t\tchain: string;\n\t\t\tdata: string | Uint8Array;\n\t }) => boolean);\n\nexport interface DevWalletConfig {\n\tadapters: SignerAdapter[];\n\t/** Network URLs by name (network → gRPC endpoint). Defaults to devnet, testnet, and localnet. */\n\tnetworks?: Record<string, string>;\n\t/** Optional per-network faucet endpoints (network → faucet URL). The\n\t * active network's faucet is resolvable via {@link DevWallet.activeFaucet}\n\t * / {@link DevWallet.getFaucet}, so a fund flow funds the SELECTED\n\t * network's account. Networks without a faucet (live mainnet, fork\n\t * stacks) simply omit a key. */\n\tfaucets?: Record<string, string>;\n\t/** Display name for the wallet. Defaults to 'Dev Wallet'. */\n\tname?: string;\n\ticon?: WalletIcon;\n\t/** Initially active network. Defaults to the first key in `networks`. */\n\tactiveNetwork?: string;\n\t/**\n\t * When set, matching requests are signed immediately without queuing for user approval.\n\t * `true` approves everything; a function is called per-request for fine-grained control.\n\t */\n\tautoApprove?: AutoApprovePolicy;\n\t/** When true, connect requests resolve immediately with all accounts. */\n\tautoConnect?: boolean;\n\t/** Factory to create a client for a given network. Defaults to SuiGrpcClient. */\n\tclientFactory?: (network: string, url: string) => ClientWithCoreApi;\n\t/**\n\t * Persist network URLs, the active network, and the UI's active account to\n\t * localStorage so they survive page reloads and are shared with popup windows.\n\t * Persisted values take precedence over `networks` and `activeNetwork`.\n\t */\n\tpersistState?: boolean;\n}\n\n/**\n * wallet-standard Wallet implementation for development. All signing requests are queued\n * and require explicit approval — either via the UI or programmatically via `approveRequest()`.\n */\nexport class DevWallet implements Wallet {\n\treadonly #adapters: SignerAdapter[];\n\t#networkUrls: Record<string, string>;\n\t#faucetUrls: Record<string, string>;\n\t/** Faucets from config; persisted faucets layer over these. */\n\treadonly #configFaucets: Record<string, string>;\n\t#clients: Record<string, ClientWithCoreApi>;\n\t#activeNetwork: string;\n\treadonly #name: string;\n\treadonly #icon: WalletIcon;\n\treadonly #autoApprove: AutoApprovePolicy;\n\treadonly #autoConnect: boolean;\n\treadonly #clientFactory: (network: string, url: string) => ClientWithCoreApi;\n\treadonly #persistState: boolean;\n\t#activeAccount: string | null;\n\t#accounts: ReadonlyWalletAccount[];\n\t#selectedAddress: string | null = null;\n\t#events: Emitter<WalletEventsMap>;\n\t#unsubscribeAdapters: (() => void)[];\n\t#pendingRequest: WalletRequest | null;\n\t#requestListeners: Set<(request: PendingSigningRequest | null) => void>;\n\t#pendingConnect: ConnectRequest | null;\n\t#connectListeners: Set<(request: PendingConnectRequest | null) => void>;\n\t/** Fired every time a dApp invokes `standard:connect` (the wallet's\n\t * authoritative \"a dApp is connecting\" signal). Distinct from\n\t * `#connectListeners`, which track the *pending-request* lifecycle for the\n\t * approval UI. Consumers (e.g. the inject's `selectAccount`) use this to\n\t * observe that the dApp has reached the wallet rather than racing a timer. */\n\t#connectedListeners: Set<() => void>;\n\t#destroyed = false;\n\n\tconstructor(config: DevWalletConfig) {\n\t\tif (config.adapters.length === 0) {\n\t\t\tconsole.warn('[dev-wallet] No adapters provided. The wallet will have no accounts.');\n\t\t}\n\t\tthis.#adapters = config.adapters;\n\t\tthis.#persistState = config.persistState ?? false;\n\t\tconst persisted = this.#loadState();\n\t\tthis.#networkUrls = persisted?.networks ?? { ...(config.networks ?? DEFAULT_NETWORK_URLS) };\n\t\tthis.#configFaucets = { ...config.faucets };\n\t\tthis.#faucetUrls = mergeFaucets(this.#configFaucets, persisted?.faucets);\n\t\tthis.#clients = {};\n\t\tthis.#activeNetwork =\n\t\t\tpersisted?.activeNetwork && persisted.activeNetwork in this.#networkUrls\n\t\t\t\t? persisted.activeNetwork\n\t\t\t\t: (config.activeNetwork ?? Object.keys(this.#networkUrls)[0] ?? '');\n\t\tthis.#activeAccount = persisted?.activeAccount ?? null;\n\t\tthis.#name = config.name ?? DEFAULT_WALLET_NAME;\n\t\tthis.#icon = config.icon ?? DEFAULT_WALLET_ICON;\n\t\tthis.#autoApprove = config.autoApprove ?? false;\n\t\tthis.#autoConnect = config.autoConnect ?? false;\n\t\tthis.#clientFactory =\n\t\t\tconfig.clientFactory ?? ((network, url) => new SuiGrpcClient({ baseUrl: url, network }));\n\t\tthis.#accounts = this.#aggregateAccounts();\n\t\tthis.#events = mitt();\n\t\tthis.#unsubscribeAdapters = [];\n\t\tthis.#pendingRequest = null;\n\t\tthis.#requestListeners = new Set();\n\t\tthis.#pendingConnect = null;\n\t\tthis.#connectListeners = new Set();\n\t\tthis.#connectedListeners = new Set();\n\n\t\tthis.#setupAdapterListeners();\n\t}\n\n\tget version() {\n\t\treturn '1.0.0' as const;\n\t}\n\n\tget name() {\n\t\treturn this.#name;\n\t}\n\n\tget icon() {\n\t\treturn this.#icon;\n\t}\n\n\t/**\n\t * Wallet-standard chains this wallet advertises. Reflects the CONFIGURED\n\t * networks — each network name is advertised as `sui:<name>` so dApp Kit's\n\t * chain-gated paths work for fork networks (`testnet-fork`, …) and custom\n\t * network names, not just the fixed standard set. Unioned with the standard\n\t * Sui chains for safety, de-duplicated, with the standard chains first and\n\t * configured-only chains appended in network-map order.\n\t */\n\tget chains(): `sui:${string}`[] {\n\t\treturn chainsForNetworks(Object.keys(this.#networkUrls));\n\t}\n\n\tget accounts(): readonly ReadonlyWalletAccount[] {\n\t\treturn this.#exposedAccounts();\n\t}\n\n\t/**\n\t * Narrow the accounts this wallet exposes to dApps (via the\n\t * wallet-standard `accounts` array + `change` events) to a single\n\t * address, or pass `null` to expose all aggregated accounts again.\n\t *\n\t * Used by the devstack page-register entry to drive headless account\n\t * selection: dApp Kit reflects the `change` event by switching its\n\t * connected account to the now-single exposed account (or, if not yet\n\t * connected, auto-connects to it). No reference to the app's dApp Kit\n\t * instance is required — selection rides the wallet-standard protocol.\n\t */\n\tsetSelectedAccount(address: string | null): void {\n\t\tif (address !== null) {\n\t\t\tconst match = this.#accounts.find((a) => a.address.toLowerCase() === address.toLowerCase());\n\t\t\tif (match === undefined) {\n\t\t\t\tconst available = this.#accounts.map((a) => a.address).join(', ');\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`No dev-wallet account for address \"${address}\". Available: ${available || '(none)'}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.#selectedAddress = match.address;\n\t\t} else {\n\t\t\tthis.#selectedAddress = null;\n\t\t}\n\t\tthis.#events.emit('change', { accounts: this.#exposedAccounts() });\n\t}\n\n\t/** Accounts exposed to dApps — narrowed to the selected address when set. */\n\t#exposedAccounts(): ReadonlyWalletAccount[] {\n\t\tif (this.#selectedAddress === null) return this.#accounts;\n\t\treturn this.#accounts.filter(\n\t\t\t(a) => a.address.toLowerCase() === this.#selectedAddress!.toLowerCase(),\n\t\t);\n\t}\n\n\tget features(): StandardConnectFeature &\n\t\tStandardEventsFeature &\n\t\tStandardDisconnectFeature &\n\t\tSuiFeatures {\n\t\treturn {\n\t\t\t'standard:connect': {\n\t\t\t\tversion: '1.0.0',\n\t\t\t\tconnect: this.#connect,\n\t\t\t},\n\t\t\t'standard:events': {\n\t\t\t\tversion: '1.0.0',\n\t\t\t\ton: this.#on,\n\t\t\t},\n\t\t\t'standard:disconnect': {\n\t\t\t\tversion: '1.0.0',\n\t\t\t\tdisconnect: this.#disconnect,\n\t\t\t},\n\t\t\t'sui:signPersonalMessage': {\n\t\t\t\tversion: '1.1.0',\n\t\t\t\tsignPersonalMessage: this.#signPersonalMessage,\n\t\t\t},\n\t\t\t'sui:signTransaction': {\n\t\t\t\tversion: '2.0.0',\n\t\t\t\tsignTransaction: this.#signTransaction,\n\t\t\t},\n\t\t\t'sui:signAndExecuteTransaction': {\n\t\t\t\tversion: '2.0.0',\n\t\t\t\tsignAndExecuteTransaction: this.#signAndExecuteTransaction,\n\t\t\t},\n\t\t};\n\t}\n\n\t/** The current pending signing request, or null if none. */\n\tget pendingRequest(): PendingSigningRequest | null {\n\t\treturn this.#pendingRequest;\n\t}\n\n\tget adapters(): readonly SignerAdapter[] {\n\t\treturn this.#adapters;\n\t}\n\n\tgetAdapterForAccount(address: string): SignerAdapter | undefined {\n\t\treturn this.#adapters.find((a) => a.getAccount(address) !== undefined);\n\t}\n\n\t/** Eagerly creates and returns clients for all configured networks. Prefer `getClient()` or `activeClient` for single-network access. */\n\tget clients(): Record<string, ClientWithCoreApi> {\n\t\tfor (const name of Object.keys(this.#networkUrls)) {\n\t\t\tthis.#ensureClient(name);\n\t\t}\n\t\treturn { ...this.#clients };\n\t}\n\n\tgetClient(network: string): ClientWithCoreApi {\n\t\treturn this.#ensureClient(network);\n\t}\n\n\tget networkUrls(): Record<string, string> {\n\t\treturn { ...this.#networkUrls };\n\t}\n\n\t/** Per-network faucet endpoints (network → faucet URL). Networks without\n\t * a faucet are absent from the map. */\n\tget faucetUrls(): Record<string, string> {\n\t\treturn { ...this.#faucetUrls };\n\t}\n\n\t/** Faucet endpoint for the named network, or `null` when that network has\n\t * no faucet configured. */\n\tgetFaucet(network: string): string | null {\n\t\treturn this.#faucetUrls[network] ?? null;\n\t}\n\n\t/** Faucet endpoint for the currently-active network, or `null` when the\n\t * active network has no faucet (live mainnet, fork stacks). Drives a\n\t * fund flow against the SELECTED network. */\n\tget activeFaucet(): string | null {\n\t\treturn this.getFaucet(this.#activeNetwork);\n\t}\n\n\tget activeNetwork(): string {\n\t\treturn this.#activeNetwork;\n\t}\n\n\tget activeClient(): ClientWithCoreApi | null {\n\t\tif (!this.#activeNetwork || !this.#networkUrls[this.#activeNetwork]) return null;\n\t\treturn this.#ensureClient(this.#activeNetwork);\n\t}\n\n\tget availableNetworks(): string[] {\n\t\treturn Object.keys(this.#networkUrls);\n\t}\n\n\t/** Switch the active network. Emits a `change` event. */\n\tsetActiveNetwork(network: string): void {\n\t\tif (!this.#networkUrls[network]) {\n\t\t\tthrow new Error(\n\t\t\t\t`No client for network \"${network}\". Available: ${this.availableNetworks.join(', ')}`,\n\t\t\t);\n\t\t}\n\t\tthis.#activeNetwork = network;\n\t\tthis.#saveState();\n\t\tthis.#events.emit('change', { accounts: this.#exposedAccounts() });\n\t}\n\n\t/** Address the wallet UI shows as active, or `null` when unset. Persisted\n\t * with `persistState`. Unlike {@link setSelectedAccount}, this doesn't\n\t * change which accounts dApps see. */\n\tget activeAccount(): string | null {\n\t\treturn this.#activeAccount;\n\t}\n\n\tsetActiveAccount(address: string | null): void {\n\t\tthis.#activeAccount = address;\n\t\tthis.#saveState();\n\t}\n\n\t/**\n\t * Add a network or replace its URL. `faucet` sets the network's faucet\n\t * endpoint; pass `null` to remove it, or omit it to leave it unchanged.\n\t */\n\taddNetwork(name: string, url: string, faucet?: string | null): void {\n\t\tassertHttpUrl(url, `network \"${name}\"`);\n\t\tif (faucet) assertHttpUrl(faucet, `the faucet of network \"${name}\"`);\n\t\tthis.#networkUrls[name] = url;\n\t\tthis.#clients[name] = this.#clientFactory(name, url);\n\t\tif (faucet) {\n\t\t\tthis.#faucetUrls[name] = faucet;\n\t\t} else if (faucet === null) {\n\t\t\tdelete this.#faucetUrls[name];\n\t\t}\n\t\tthis.#saveState();\n\t\tthis.#events.emit('change', { accounts: this.#exposedAccounts() });\n\t}\n\n\tremoveNetwork(name: string): void {\n\t\tdelete this.#networkUrls[name];\n\t\tdelete this.#faucetUrls[name];\n\t\tdelete this.#clients[name];\n\t\tif (this.#activeNetwork === name) {\n\t\t\tthis.#activeNetwork = Object.keys(this.#networkUrls)[0] ?? '';\n\t\t}\n\t\tthis.#saveState();\n\t\tthis.#events.emit('change', { accounts: this.#exposedAccounts() });\n\t}\n\n\t/** Register this wallet with the wallet-standard registry. Returns an unregister function. */\n\tregister(): () => void {\n\t\tif (typeof process !== 'undefined' && process.env?.NODE_ENV === 'production') {\n\t\t\tconsole.warn(\n\t\t\t\t'[@mysten-incubation/dev-wallet] DevWallet is intended for development only. Do not use in production.',\n\t\t\t);\n\t\t}\n\t\tconst walletsApi = getWallets();\n\t\treturn walletsApi.register(this);\n\t}\n\n\t/** Reject any pending requests, unsubscribe from adapters, destroy them, and clear all listeners. */\n\tdestroy(): void {\n\t\tthis.#destroyed = true;\n\t\tfor (const unsub of this.#unsubscribeAdapters) {\n\t\t\tunsub();\n\t\t}\n\t\tthis.#unsubscribeAdapters = [];\n\t\tfor (const adapter of this.#adapters) {\n\t\t\tadapter.destroy();\n\t\t}\n\t\tif (this.#pendingRequest) {\n\t\t\tthis.#pendingRequest.reject(new Error('Wallet destroyed.'));\n\t\t\tthis.#pendingRequest = null;\n\t\t}\n\t\tif (this.#pendingConnect) {\n\t\t\tthis.#pendingConnect.reject(new Error('Wallet destroyed.'));\n\t\t\tthis.#pendingConnect = null;\n\t\t}\n\t\tthis.#requestListeners.clear();\n\t\tthis.#connectListeners.clear();\n\t\tthis.#connectedListeners.clear();\n\t\tthis.#events.all.clear();\n\t}\n\n\t/** Sign the pending request using the account's adapter signer and resolve the promise. */\n\tasync approveRequest(): Promise<void> {\n\t\tconst request = this.#pendingRequest;\n\t\tif (!request) {\n\t\t\tthrow new Error('No pending request to approve.');\n\t\t}\n\n\t\ttry {\n\t\t\tconst result = await this.#executeSigning(\n\t\t\t\trequest.type,\n\t\t\t\trequest.account,\n\t\t\t\trequest.chain,\n\t\t\t\trequest.data,\n\t\t\t);\n\t\t\tthis.#pendingRequest = null;\n\t\t\tthis.#notifyRequestListeners();\n\t\t\trequest.resolve(result);\n\t\t} catch (error) {\n\t\t\tthis.#pendingRequest = null;\n\t\t\tthis.#notifyRequestListeners();\n\t\t\trequest.reject(error instanceof Error ? error : new Error(String(error)));\n\t\t}\n\t}\n\n\trejectRequest(reason?: string): void {\n\t\tconst request = this.#pendingRequest;\n\t\tif (!request) {\n\t\t\tthrow new Error('No pending request to reject.');\n\t\t}\n\t\tthis.#pendingRequest = null;\n\t\tthis.#notifyRequestListeners();\n\t\trequest.reject(new Error(reason ?? 'Request rejected by user.'));\n\t}\n\n\t/** Subscribe to pending request changes. Returns an unsubscribe function. */\n\tonRequestChange(callback: (request: PendingSigningRequest | null) => void): () => void {\n\t\tthis.#requestListeners.add(callback);\n\t\treturn () => {\n\t\t\tthis.#requestListeners.delete(callback);\n\t\t};\n\t}\n\n\t/** The current pending connect request, or null if none. */\n\tget pendingConnect(): PendingConnectRequest | null {\n\t\treturn this.#pendingConnect;\n\t}\n\n\t/** Expose the given addresses to the dApp. Pass an empty array to expose all accounts. */\n\tapproveConnect(selectedAddresses: string[]): void {\n\t\tconst request = this.#pendingConnect;\n\t\tif (!request) {\n\t\t\tthrow new Error('No pending connect request to approve.');\n\t\t}\n\n\t\tconst selected =\n\t\t\tselectedAddresses.length > 0\n\t\t\t\t? this.#accounts.filter((a) => selectedAddresses.includes(a.address))\n\t\t\t\t: [...this.#accounts];\n\n\t\tthis.#pendingConnect = null;\n\t\tthis.#notifyConnectListeners();\n\t\trequest.resolve({ accounts: selected });\n\t}\n\n\trejectConnect(reason?: string): void {\n\t\tconst request = this.#pendingConnect;\n\t\tif (!request) {\n\t\t\tthrow new Error('No pending connect request to reject.');\n\t\t}\n\t\tthis.#pendingConnect = null;\n\t\tthis.#notifyConnectListeners();\n\t\trequest.reject(new Error(reason ?? 'Connection rejected by user.'));\n\t}\n\n\t/** Subscribe to pending connect request changes. Returns an unsubscribe function. */\n\tonConnectChange(callback: (request: PendingConnectRequest | null) => void): () => void {\n\t\tthis.#connectListeners.add(callback);\n\t\treturn () => {\n\t\t\tthis.#connectListeners.delete(callback);\n\t\t};\n\t}\n\n\t#notifyRequestListeners() {\n\t\tconst request = this.#pendingRequest;\n\t\tfor (const listener of this.#requestListeners) {\n\t\t\tlistener(request);\n\t\t}\n\t}\n\n\t#notifyConnectListeners() {\n\t\tconst request = this.#pendingConnect;\n\t\tfor (const listener of this.#connectListeners) {\n\t\t\tlistener(request);\n\t\t}\n\t}\n\n\t/**\n\t * Subscribe to the \"a dApp invoked `standard:connect`\" edge. Returns an\n\t * unsubscribe function. Used by the page-register entry to gate its\n\t * narrow→widen account-selection on an OBSERVED connect rather than a fixed\n\t * timer (see `src/inject/index.ts`).\n\t */\n\tonDAppConnected(callback: () => void): () => void {\n\t\tthis.#connectedListeners.add(callback);\n\t\treturn () => {\n\t\t\tthis.#connectedListeners.delete(callback);\n\t\t};\n\t}\n\n\t#notifyConnectedListeners() {\n\t\tfor (const listener of this.#connectedListeners) {\n\t\t\ttry {\n\t\t\t\tlistener();\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('[dev-wallet] onDAppConnected listener threw:', error);\n\t\t\t}\n\t\t}\n\t}\n\n\t#aggregateAccounts(): ReadonlyWalletAccount[] {\n\t\treturn this.#adapters.flatMap((a) => a.getAccounts().map((acc) => acc.walletAccount));\n\t}\n\n\t#setupAdapterListeners() {\n\t\tfor (const adapter of this.#adapters) {\n\t\t\tconst unsub = adapter.onAccountsChanged(() => {\n\t\t\t\tthis.#accounts = this.#aggregateAccounts();\n\t\t\t\tthis.#events.emit('change', { accounts: this.#exposedAccounts() });\n\t\t\t});\n\t\t\tthis.#unsubscribeAdapters.push(unsub);\n\t\t}\n\t}\n\n\t#on: StandardEventsOnMethod = (event, listener) => {\n\t\tthis.#events.on(event, listener);\n\t\treturn () => this.#events.off(event, listener);\n\t};\n\n\t#connect: StandardConnectMethod = async () => {\n\t\t// Notify observers that a dApp has invoked `standard:connect`. This is\n\t\t// the wallet's authoritative \"a dApp reached us\" edge — fired before we\n\t\t// branch on auto/queued so it covers both the silent auto-connect path\n\t\t// and an interactive connect.\n\t\tthis.#notifyConnectedListeners();\n\n\t\t// Auto-connect: return all accounts immediately\n\t\tif (this.#autoConnect) {\n\t\t\treturn { accounts: this.accounts };\n\t\t}\n\n\t\t// Queue a connect request for user to select accounts\n\t\tif (this.#pendingConnect) {\n\t\t\treturn Promise.reject(new Error('A connect request is already pending.'));\n\t\t}\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tthis.#pendingConnect = {\n\t\t\t\tid: crypto.randomUUID(),\n\t\t\t\tresolve,\n\t\t\t\treject,\n\t\t\t};\n\t\t\tthis.#notifyConnectListeners();\n\t\t});\n\t};\n\n\t#disconnect: StandardDisconnectMethod = async () => {\n\t\t// Disconnecting ends the dApp session but does not affect wallet state.\n\t\t// The wallet retains its accounts and adapter subscriptions.\n\t};\n\n\t#getClientForChain(chain: string) {\n\t\tconst network = getNetworkFromChain(chain);\n\t\tif (!network) {\n\t\t\tthrow new Error(`Invalid chain identifier: ${chain}`);\n\t\t}\n\t\treturn this.#ensureClient(network);\n\t}\n\n\t#loadState(): PersistedStateV1 | null {\n\t\tif (!this.#persistState || typeof localStorage === 'undefined') return null;\n\t\treturn readPersisted(localStorage, STATE_STORAGE_KEY, decodePersistedState);\n\t}\n\n\t/** Current faucets plus an empty-string marker for each config faucet the\n\t * user removed, so the removal survives a reload. */\n\t#persistableFaucets(): Record<string, string> {\n\t\tconst faucets = { ...this.#faucetUrls };\n\t\tfor (const name of Object.keys(this.#configFaucets)) {\n\t\t\tif (!(name in faucets)) faucets[name] = '';\n\t\t}\n\t\treturn faucets;\n\t}\n\n\t#saveState(): void {\n\t\tif (!this.#persistState || typeof localStorage === 'undefined') return;\n\t\tconst state: PersistedStateV1 = {\n\t\t\tversion: 1,\n\t\t\tnetworks: this.#networkUrls,\n\t\t\tfaucets: this.#persistableFaucets(),\n\t\t\tactiveNetwork: this.#activeNetwork,\n\t\t\t...(this.#activeAccount ? { activeAccount: this.#activeAccount } : {}),\n\t\t};\n\t\tlocalStorage.setItem(STATE_STORAGE_KEY, JSON.stringify(state));\n\t}\n\n\t#ensureClient(network: string): ClientWithCoreApi {\n\t\tif (!this.#clients[network]) {\n\t\t\tconst url = this.#networkUrls[network];\n\t\t\tif (!url) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`No client for network \"${network}\". Available: ${this.availableNetworks.join(', ')}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.#clients[network] = this.#clientFactory(network, url);\n\t\t}\n\t\treturn this.#clients[network];\n\t}\n\n\t#getSigner(address: string) {\n\t\tfor (const adapter of this.#adapters) {\n\t\t\tconst account = adapter.getAccount(address);\n\t\t\tif (account) return account.signer;\n\t\t}\n\t\tconst allAddresses = this.#adapters.flatMap((a) => a.getAccounts().map((acc) => acc.address));\n\t\tthrow new Error(\n\t\t\t`No account found for address \"${address}\". Available addresses: ${allAddresses.join(', ')}`,\n\t\t);\n\t}\n\n\t#shouldAutoApprove(\n\t\ttype: WalletRequest['type'],\n\t\taccount: WalletAccount,\n\t\tchain: string,\n\t\tdata: string | Uint8Array,\n\t): boolean {\n\t\t// Check adapter-level opt-out first — CLI adapters never auto-sign\n\t\tconst adapter = this.getAdapterForAccount(account.address);\n\t\tif (adapter?.allowAutoSign === false) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (this.#autoApprove === true) {\n\t\t\treturn true;\n\t\t}\n\t\tif (typeof this.#autoApprove === 'function') {\n\t\t\treturn this.#autoApprove({ type, account, chain, data });\n\t\t}\n\t\treturn false;\n\t}\n\n\tasync #executeSigning(\n\t\ttype: WalletRequest['type'],\n\t\taccount: WalletAccount,\n\t\tchain: string,\n\t\tdata: string | Uint8Array,\n\t) {\n\t\tconst signer = this.#getSigner(account.address);\n\t\tconst client = type !== 'sign-personal-message' ? this.#getClientForChain(chain) : undefined;\n\t\treturn executeSigning({ type, signer, data, client });\n\t}\n\n\t#enqueueRequest<T extends WalletRequest['type']>(\n\t\ttype: T,\n\t\taccount: WalletAccount,\n\t\tchain: string,\n\t\tdata: string | Uint8Array,\n\t): Promise<Extract<SigningResult, { type: T }>> {\n\t\ttype Result = Extract<SigningResult, { type: T }>;\n\t\tif (this.#destroyed) {\n\t\t\treturn Promise.reject(new Error('Wallet has been destroyed.'));\n\t\t}\n\t\tif (this.#shouldAutoApprove(type, account, chain, data)) {\n\t\t\treturn this.#executeSigning(type, account, chain, data) as Promise<Result>;\n\t\t}\n\n\t\t// Only one request at a time — subsequent requests are immediately rejected.\n\t\t// DApps that batch transactions should serialize their signing calls.\n\t\tif (this.#pendingRequest) {\n\t\t\treturn Promise.reject(new Error('A signing request is already pending.'));\n\t\t}\n\n\t\treturn new Promise<Result>((resolve, reject) => {\n\t\t\tthis.#pendingRequest = {\n\t\t\t\tid: crypto.randomUUID(),\n\t\t\t\ttype,\n\t\t\t\taccount,\n\t\t\t\tchain,\n\t\t\t\tdata,\n\t\t\t\tresolve: resolve as (result: SigningResult) => void,\n\t\t\t\treject,\n\t\t\t};\n\t\t\tthis.#notifyRequestListeners();\n\t\t});\n\t}\n\n\t#signPersonalMessage: SuiSignPersonalMessageMethod = async (input) => {\n\t\treturn this.#enqueueRequest(\n\t\t\t'sign-personal-message',\n\t\t\tinput.account,\n\t\t\tinput.chain ?? 'sui:unknown',\n\t\t\tinput.message,\n\t\t);\n\t};\n\n\t#signTransaction: SuiSignTransactionMethod = async (input) => {\n\t\tconst txJson = await input.transaction.toJSON();\n\t\treturn this.#enqueueRequest('sign-transaction', input.account, input.chain, txJson);\n\t};\n\n\t#signAndExecuteTransaction: SuiSignAndExecuteTransactionMethod = async (input) => {\n\t\tconst txJson = await input.transaction.toJSON();\n\t\treturn this.#enqueueRequest('sign-and-execute-transaction', input.account, input.chain, txJson);\n\t};\n}\n"],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ //#region src/wallet/persisted.ts
2
+ /**
3
+ * Read a versioned JSON value from storage. A missing key is a plain miss;
4
+ * unparseable data, or data `decode` rejects (returns `null`), is a miss plus
5
+ * a warning, so a bad write never crashes the wallet.
6
+ */
7
+ function readPersisted(storage, key, decode) {
8
+ const raw = storage.getItem(key);
9
+ if (raw === null) return null;
10
+ let value = null;
11
+ try {
12
+ value = decode(JSON.parse(raw));
13
+ } catch {}
14
+ if (value === null) console.warn(`[dev-wallet] Ignoring invalid data in "${key}".`);
15
+ return value;
16
+ }
17
+ //#endregion
18
+ export { readPersisted };
19
+
20
+ //# sourceMappingURL=persisted.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persisted.mjs","names":[],"sources":["../../src/wallet/persisted.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Read a versioned JSON value from storage. A missing key is a plain miss;\n * unparseable data, or data `decode` rejects (returns `null`), is a miss plus\n * a warning, so a bad write never crashes the wallet.\n */\nexport function readPersisted<T>(\n\tstorage: Storage,\n\tkey: string,\n\tdecode: (value: unknown) => T | null,\n): T | null {\n\tconst raw = storage.getItem(key);\n\tif (raw === null) return null;\n\tlet value: T | null = null;\n\ttry {\n\t\tvalue = decode(JSON.parse(raw));\n\t} catch {\n\t\t// Invalid JSON — handled below.\n\t}\n\tif (value === null) console.warn(`[dev-wallet] Ignoring invalid data in \"${key}\".`);\n\treturn value;\n}\n"],"mappings":";;;;;;AAQA,SAAgB,cACf,SACA,KACA,QACW;CACX,MAAM,MAAM,QAAQ,QAAQ,GAAG;CAC/B,IAAI,QAAQ,MAAM,OAAO;CACzB,IAAI,QAAkB;CACtB,IAAI;EACH,QAAQ,OAAO,KAAK,MAAM,GAAG,CAAC;CAC/B,QAAQ,CAER;CACA,IAAI,UAAU,MAAM,QAAQ,KAAK,0CAA0C,IAAI,GAAG;CAClF,OAAO;AACR"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mysten-incubation/dev-wallet",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "A modular dev wallet for Sui",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Mysten Labs <build@mystenlabs.com>",
@@ -73,23 +73,23 @@
73
73
  "@testing-library/react": "^16.3.1",
74
74
  "@types/node": "^25.9.3",
75
75
  "@types/react": "^19.2.17",
76
- "@vitest/browser": "^4.1.8",
77
- "@vitest/browser-playwright": "^4.1.8",
76
+ "@vitest/browser": "^4.1.11",
77
+ "@vitest/browser-playwright": "^4.1.11",
78
78
  "happy-dom": "^20.10.3",
79
79
  "playwright": "^1.60.0",
80
80
  "react": "^19.2.7",
81
81
  "react-dom": "^19.2.7",
82
82
  "typescript": "^6.0.3",
83
83
  "vite": "^8.0.16",
84
- "vitest": "^4.1.8"
84
+ "vitest": "^4.1.11"
85
85
  },
86
86
  "dependencies": {
87
- "@hono/node-server": "^2.0.4",
87
+ "@hono/node-server": "^2.1.1",
88
88
  "@mysten/utils": "^0.4.0",
89
89
  "@mysten/wallet-sdk": "^0.4.0",
90
90
  "@mysten/wallet-standard": "^0.21.0",
91
91
  "@mysten/window-wallet-core": "^0.2.0",
92
- "hono": "^4.12.25",
92
+ "hono": "^4.13.5",
93
93
  "lit": "^3.3.3"
94
94
  },
95
95
  "peerDependencies": {