@mysten-incubation/dev-wallet 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/dist/adapters/build-managed-account.d.mts +15 -2
  3. package/dist/adapters/build-managed-account.d.mts.map +1 -1
  4. package/dist/adapters/build-managed-account.mjs +28 -4
  5. package/dist/adapters/build-managed-account.mjs.map +1 -1
  6. package/dist/adapters/devstack-adapter.d.mts +19 -7
  7. package/dist/adapters/devstack-adapter.d.mts.map +1 -1
  8. package/dist/adapters/devstack-adapter.mjs +16 -8
  9. package/dist/adapters/devstack-adapter.mjs.map +1 -1
  10. package/dist/inject/index.d.mts +37 -16
  11. package/dist/inject/index.d.mts.map +1 -1
  12. package/dist/inject/index.mjs +20 -6
  13. package/dist/inject/index.mjs.map +1 -1
  14. package/dist/standalone/assets/{base-adapter-DET6CP7z.js → base-adapter-C5w5q7XZ.js} +1 -1
  15. package/dist/standalone/assets/{in-memory-adapter-CX7xym4e.js → in-memory-adapter-Ban2kRYS.js} +1 -1
  16. package/dist/standalone/assets/{main-CuOnBmaa.js → main-xfzarPod.js} +3 -3
  17. package/dist/standalone/assets/{webcrypto-adapter-CUQmbHDy.js → webcrypto-adapter-Ddqn-RAu.js} +1 -1
  18. package/dist/standalone/index.html +2 -2
  19. package/dist/ui/dev-wallet-balances.d.mts +7 -0
  20. package/dist/ui/dev-wallet-balances.d.mts.map +1 -1
  21. package/dist/ui/dev-wallet-balances.mjs +50 -3
  22. package/dist/ui/dev-wallet-balances.mjs.map +1 -1
  23. package/dist/ui/wallet-controller.mjs +2 -1
  24. package/dist/ui/wallet-controller.mjs.map +1 -1
  25. package/dist/wallet/dev-wallet.d.mts +26 -2
  26. package/dist/wallet/dev-wallet.d.mts.map +1 -1
  27. package/dist/wallet/dev-wallet.mjs +35 -6
  28. package/dist/wallet/dev-wallet.mjs.map +1 -1
  29. package/package.json +1 -1
  30. package/src/adapters/build-managed-account.ts +28 -2
  31. package/src/adapters/devstack-adapter.ts +28 -8
  32. package/src/inject/index.ts +98 -33
  33. package/src/ui/dev-wallet-balances.ts +62 -3
  34. package/src/ui/wallet-controller.ts +2 -1
  35. package/src/wallet/dev-wallet.ts +47 -7
@@ -1 +1 @@
1
- import{a as e,l as t,n,t as r}from"./base-adapter-DET6CP7z.js";import{t as i}from"./main-CuOnBmaa.js";function a(e){let t=new Uint8Array(e),n=t.slice(1,33),r=t.slice(33,65)[31]&1?3:2,a=new Uint8Array(i.SIZE);return a[0]=r,a.set(n,1),a}var o=class n extends e{#e;static async generate({extractable:e=!1}={}){let t=await globalThis.crypto.subtle.generateKey({name:`ECDSA`,namedCurve:`P-256`},e,[`sign`,`verify`]),r=await globalThis.crypto.subtle.exportKey(`raw`,t.publicKey);return new n(t.privateKey,a(new Uint8Array(r)))}static import(e){return new n(e.privateKey,e.publicKey)}getKeyScheme(){return`Secp256r1`}constructor(e,t){super(),this.privateKey=e,this.#e=new i(t)}export(){let e={privateKey:this.privateKey,publicKey:this.#e.toRawBytes()};return Object.defineProperty(e,"toJSON",{enumerable:!1,value:()=>{throw Error(`The exported keypair must not be serialized. It must be stored in IndexedDB directly.`)}}),e}getPublicKey(){return this.#e}async sign(e){let n=await globalThis.crypto.subtle.sign({name:`ECDSA`,hash:`SHA-256`},this.privateKey,e),r=t.Signature.fromBytes(new Uint8Array(n));return(r.hasHighS()?new t.Signature(r.r,t.Point.Fn.neg(r.s)):r).toBytes(`compact`)}},s=class{#e;#t;#n=null;constructor(e,t){this.#e=e,this.#t=t}get(e){return this.#i(`readonly`,t=>t.get(e))}async set(e,t){await this.#i(`readwrite`,n=>n.put(t,e))}del(e){return this.#i(`readwrite`,t=>t.delete(e))}entries(){return this.#r().then(e=>new Promise((t,n)=>{let r=e.transaction(this.#t,`readonly`),i=r.objectStore(this.#t),a=[],o=i.openCursor();o.onsuccess=()=>{let e=o.result;e&&(a.push([e.key,e.value]),e.continue())},r.oncomplete=()=>t(a),r.onerror=()=>n(r.error)}))}#r(){return this.#n||=new Promise((e,t)=>{let n=indexedDB.open(this.#e);n.onupgradeneeded=()=>{n.result.objectStoreNames.contains(this.#t)||n.result.createObjectStore(this.#t)},n.onsuccess=()=>e(n.result),n.onerror=()=>{this.#n=null,t(n.error)}}),this.#n}#i(e,t){return this.#r().then(n=>new Promise((r,i)=>{let a=n.transaction(this.#t,e),o=t(a.objectStore(this.#t));a.oncomplete=()=>r(o.result),a.onerror=()=>i(a.error),a.onabort=()=>i(a.error)}))}},c=`dev-wallet-webcrypto`,l=`accounts`,u=`__account_meta__`,d=class extends r{#e;constructor(e){super(),this.id=`webcrypto`,this.name=`WebCrypto Signer`,this.#e=new s(e?.dbName??c,e?.storeName??l)}async initialize(){let e=await this.#e.entries(),t=await this.#e.get(u)??[],r=[];for(let[i,a]of e){if(i===u)continue;let e=i;try{let i=a,s=o.import(i),c=t.find(t=>t.address===e);r.push(n(s,e,c?.label??`Account`))}catch(t){console.warn(`[dev-wallet] Failed to load account ${e}, skipping:`,t)}}this.setInitialAccounts(r)}async createAccount(e){let t=await o.generate(),r=t.getPublicKey().toSuiAddress(),i=n(t,r,e?.label??this.getDefaultLabel());return await this.#e.set(r,t.export()),this.addAccount(i),await this.#t(),i}async renameAccount(e,t){return this._performRename(e,t)?(await this.#t(),!0):!1}async removeAccount(e){return this.removeAccountByAddress(e)?(await this.#e.del(e),await this.#t(),!0):!1}async#t(){let e=this.getAccounts().map(e=>({address:e.address,label:e.label}));await this.#e.set(u,e)}};export{d as WebCryptoSignerAdapter};
1
+ import{a as e,l as t,n,t as r}from"./base-adapter-C5w5q7XZ.js";import{t as i}from"./main-xfzarPod.js";function a(e){let t=new Uint8Array(e),n=t.slice(1,33),r=t.slice(33,65)[31]&1?3:2,a=new Uint8Array(i.SIZE);return a[0]=r,a.set(n,1),a}var o=class n extends e{#e;static async generate({extractable:e=!1}={}){let t=await globalThis.crypto.subtle.generateKey({name:`ECDSA`,namedCurve:`P-256`},e,[`sign`,`verify`]),r=await globalThis.crypto.subtle.exportKey(`raw`,t.publicKey);return new n(t.privateKey,a(new Uint8Array(r)))}static import(e){return new n(e.privateKey,e.publicKey)}getKeyScheme(){return`Secp256r1`}constructor(e,t){super(),this.privateKey=e,this.#e=new i(t)}export(){let e={privateKey:this.privateKey,publicKey:this.#e.toRawBytes()};return Object.defineProperty(e,"toJSON",{enumerable:!1,value:()=>{throw Error(`The exported keypair must not be serialized. It must be stored in IndexedDB directly.`)}}),e}getPublicKey(){return this.#e}async sign(e){let n=await globalThis.crypto.subtle.sign({name:`ECDSA`,hash:`SHA-256`},this.privateKey,e),r=t.Signature.fromBytes(new Uint8Array(n));return(r.hasHighS()?new t.Signature(r.r,t.Point.Fn.neg(r.s)):r).toBytes(`compact`)}},s=class{#e;#t;#n=null;constructor(e,t){this.#e=e,this.#t=t}get(e){return this.#i(`readonly`,t=>t.get(e))}async set(e,t){await this.#i(`readwrite`,n=>n.put(t,e))}del(e){return this.#i(`readwrite`,t=>t.delete(e))}entries(){return this.#r().then(e=>new Promise((t,n)=>{let r=e.transaction(this.#t,`readonly`),i=r.objectStore(this.#t),a=[],o=i.openCursor();o.onsuccess=()=>{let e=o.result;e&&(a.push([e.key,e.value]),e.continue())},r.oncomplete=()=>t(a),r.onerror=()=>n(r.error)}))}#r(){return this.#n||=new Promise((e,t)=>{let n=indexedDB.open(this.#e);n.onupgradeneeded=()=>{n.result.objectStoreNames.contains(this.#t)||n.result.createObjectStore(this.#t)},n.onsuccess=()=>e(n.result),n.onerror=()=>{this.#n=null,t(n.error)}}),this.#n}#i(e,t){return this.#r().then(n=>new Promise((r,i)=>{let a=n.transaction(this.#t,e),o=t(a.objectStore(this.#t));a.oncomplete=()=>r(o.result),a.onerror=()=>i(a.error),a.onabort=()=>i(a.error)}))}},c=`dev-wallet-webcrypto`,l=`accounts`,u=`__account_meta__`,d=class extends r{#e;constructor(e){super(),this.id=`webcrypto`,this.name=`WebCrypto Signer`,this.#e=new s(e?.dbName??c,e?.storeName??l)}async initialize(){let e=await this.#e.entries(),t=await this.#e.get(u)??[],r=[];for(let[i,a]of e){if(i===u)continue;let e=i;try{let i=a,s=o.import(i),c=t.find(t=>t.address===e);r.push(n(s,e,c?.label??`Account`))}catch(t){console.warn(`[dev-wallet] Failed to load account ${e}, skipping:`,t)}}this.setInitialAccounts(r)}async createAccount(e){let t=await o.generate(),r=t.getPublicKey().toSuiAddress(),i=n(t,r,e?.label??this.getDefaultLabel());return await this.#e.set(r,t.export()),this.addAccount(i),await this.#t(),i}async renameAccount(e,t){return this._performRename(e,t)?(await this.#t(),!0):!1}async removeAccount(e){return this.removeAccountByAddress(e)?(await this.#e.del(e),await this.#t(),!0):!1}async#t(){let e=this.getAccounts().map(e=>({address:e.address,label:e.label}));await this.#e.set(u,e)}};export{d as WebCryptoSignerAdapter};
@@ -37,8 +37,8 @@
37
37
  font-size: 14px;
38
38
  }
39
39
  </style>
40
- <script type="module" crossorigin src="/assets/main-CuOnBmaa.js"></script>
41
- <link rel="modulepreload" crossorigin href="/assets/base-adapter-DET6CP7z.js">
40
+ <script type="module" crossorigin src="/assets/main-xfzarPod.js"></script>
41
+ <link rel="modulepreload" crossorigin href="/assets/base-adapter-C5w5q7XZ.js">
42
42
  </head>
43
43
  <body>
44
44
  <div id="app">
@@ -9,6 +9,11 @@ declare class DevWalletBalances extends LitElement {
9
9
  address: string;
10
10
  client: ClientWithCoreApi | null;
11
11
  network: string;
12
+ /** Faucet endpoint for the active network (the wallet's `activeFaucet`),
13
+ * or `null` when the active network has no faucet (live mainnet, fork
14
+ * stacks). When set, a "Request from faucet" button funds the active
15
+ * address against this endpoint and refreshes balances. */
16
+ faucetHost: string | null;
12
17
  /** Optional pre-seeded coin metadata — pass the generated `coins`
13
18
  * constant from devstack codegen (`src/generated/coins.ts`) to skip
14
19
  * per-coin `getCoinMetadata` RPC waterfalls on UI load. Unknown coin
@@ -17,6 +22,8 @@ declare class DevWalletBalances extends LitElement {
17
22
  private _balances;
18
23
  private _loading;
19
24
  private _error;
25
+ private _funding;
26
+ private _fundError;
20
27
  /** Re-fetch balances for the current address/client. */
21
28
  refresh(): void;
22
29
  willUpdate(changedProperties: Map<string, unknown>): void;
@@ -1 +1 @@
1
- {"version":3,"file":"dev-wallet-balances.d.mts","names":[],"sources":["../../src/ui/dev-wallet-balances.ts"],"mappings":";;;;;cA0Ba,iBAAA,SAA0B,UAAA;EAAA;SACtB,MAAA,iBAAM,SAAA,iBAAA,SAAA;EA2LtB,OAAA;EAGA,MAAA,EAAQ,iBAAA;EAGR,OAAA;EAOO;;;;EAAP,KAAA,EAAO,UAAA;EAAA,QAGC,SAAA;EAAA,QAGA,QAAA;EAAA,QAGA,MAAA;EAjNQ;EA0NhB,OAAA;EAMS,UAAA,CAAW,iBAAA,EAAmB,GAAA;EAe9B,MAAA,kBAAM,cAAA,aAAA,OAAA;AAAA;AAAA,QAyHR,MAAA;EAAA,UACG,qBAAA;IACT,qBAAA,EAAuB,iBAAiB;EAAA;AAAA"}
1
+ {"version":3,"file":"dev-wallet-balances.d.mts","names":[],"sources":["../../src/ui/dev-wallet-balances.ts"],"mappings":";;;;;cA2Ba,iBAAA,SAA0B,UAAA;EAAA;SACtB,MAAA,iBAAM,SAAA,iBAAA,SAAA;EAkMtB,OAAA;EAGA,MAAA,EAAQ,iBAAA;EAGR,OAAA;EAcO;;;;EAPP,UAAA;EAhNgD;;;;EAuNhD,KAAA,EAAO,UAAA;EAAA,QAGC,SAAA;EAAA,QAGA,QAAA;EAAA,QAGA,MAAA;EAAA,QAGA,QAAA;EAAA,QAGA,UAAA;EAtBR;EA+BA,OAAA;EAwBS,UAAA,CAAW,iBAAA,EAAmB,GAAA;EAe9B,MAAA,kBAAM,cAAA,aAAA,OAAA;AAAA;AAAA,QA6IR,MAAA;EAAA,UACG,qBAAA;IACT,qBAAA,EAAuB,iBAAiB;EAAA;AAAA"}
@@ -3,6 +3,7 @@ import { formatAddress, formatCoinBalance, getCoinSymbol, indexCoinsByType, isSu
3
3
  import { __decorate } from "../_virtual/_@oxc-project_runtime@0.135.0/helpers/esm/decorate.mjs";
4
4
  import { LitElement, css, html, nothing } from "lit";
5
5
  import { customElement, property, state } from "lit/decorators.js";
6
+ import { requestSuiFromFaucetV2 } from "@mysten/sui/faucet";
6
7
  //#region src/ui/dev-wallet-balances.ts
7
8
  let DevWalletBalances = class DevWalletBalances extends LitElement {
8
9
  constructor(..._args) {
@@ -10,10 +11,13 @@ let DevWalletBalances = class DevWalletBalances extends LitElement {
10
11
  this.address = "";
11
12
  this.client = null;
12
13
  this.network = "";
14
+ this.faucetHost = null;
13
15
  this.coins = null;
14
16
  this._balances = [];
15
17
  this._loading = false;
16
18
  this._error = null;
19
+ this._funding = false;
20
+ this._fundError = null;
17
21
  this.#lastFetchedAddress = "";
18
22
  this.#lastFetchedClient = null;
19
23
  this.#fetchGeneration = 0;
@@ -158,6 +162,13 @@ let DevWalletBalances = class DevWalletBalances extends LitElement {
158
162
  min-width: 0;
159
163
  }
160
164
 
165
+ .hero-actions {
166
+ display: flex;
167
+ align-items: center;
168
+ gap: 6px;
169
+ flex-shrink: 0;
170
+ }
171
+
161
172
  .hero-balance {
162
173
  display: flex;
163
174
  align-items: baseline;
@@ -216,6 +227,25 @@ let DevWalletBalances = class DevWalletBalances extends LitElement {
216
227
  refresh() {
217
228
  if (this.address && this.client) this.#fetchBalances();
218
229
  }
230
+ /** Request SUI from the active network's faucet for the active address,
231
+ * then refresh balances. No-op when no `faucetHost` is configured (live
232
+ * mainnet / fork stacks have none). */
233
+ async #requestFromFaucet() {
234
+ if (!this.faucetHost || !this.address || this._funding) return;
235
+ this._funding = true;
236
+ this._fundError = null;
237
+ try {
238
+ await requestSuiFromFaucetV2({
239
+ host: this.faucetHost,
240
+ recipient: this.address
241
+ });
242
+ this.refresh();
243
+ } catch (error) {
244
+ this._fundError = error instanceof Error ? error.message : "Faucet request failed. Try again.";
245
+ } finally {
246
+ this._funding = false;
247
+ }
248
+ }
219
249
  willUpdate(changedProperties) {
220
250
  if (changedProperties.has("coins") && this.coins !== this.#lastCoinsRef) {
221
251
  this.#coinIndex = indexCoinsByType(this.coins);
@@ -237,8 +267,21 @@ let DevWalletBalances = class DevWalletBalances extends LitElement {
237
267
  </div>
238
268
  <div class="hero-note">${formatAddress(this.address)} · ${networkLabel}</div>
239
269
  </div>
240
- <button class="action-btn" @click=${this.refresh}>Refresh</button>
270
+ <div class="hero-actions">
271
+ ${this.faucetHost ? html`<button
272
+ class="action-btn"
273
+ part="faucet-button"
274
+ ?disabled=${this._funding}
275
+ @click=${this.#requestFromFaucet}
276
+ >
277
+ ${this._funding ? "Funding..." : "Faucet"}
278
+ </button>` : nothing}
279
+ <button class="action-btn" @click=${this.refresh}>Refresh</button>
280
+ </div>
241
281
  </div>
282
+ ${this._fundError ? html`<div class="error-state" part="faucet-error-message" aria-live="polite">
283
+ ${this._fundError}
284
+ </div>` : nothing}
242
285
  <div class="coins-header">
243
286
  <h3 class="section-header">Coins · ${this._balances.length}</h3>
244
287
  <button class="manage-btn" disabled>Manage</button>
@@ -248,8 +291,9 @@ let DevWalletBalances = class DevWalletBalances extends LitElement {
248
291
  </div>` : this._balances.length === 0 ? html`<div class="empty-state empty-wallet" part="empty-state">
249
292
  <div class="empty-title">No balances on ${networkLabel}</div>
250
293
  <div class="empty-copy">
251
- Fund ${formatAddress(this.address)} from your local faucet or devstack seed, then
252
- refresh balances here.
294
+ ${this.faucetHost ? html`Use the Faucet button above to fund ${formatAddress(this.address)} on
295
+ ${networkLabel}, then refresh.` : html`Fund ${formatAddress(this.address)} from your local faucet or devstack
296
+ seed, then refresh balances here.`}
253
297
  </div>
254
298
  </div>` : html`
255
299
  <div class="balance-list" part="balance-list">
@@ -317,10 +361,13 @@ let DevWalletBalances = class DevWalletBalances extends LitElement {
317
361
  __decorate([property({ type: String })], DevWalletBalances.prototype, "address", void 0);
318
362
  __decorate([property({ attribute: false })], DevWalletBalances.prototype, "client", void 0);
319
363
  __decorate([property({ type: String })], DevWalletBalances.prototype, "network", void 0);
364
+ __decorate([property({ attribute: false })], DevWalletBalances.prototype, "faucetHost", void 0);
320
365
  __decorate([property({ attribute: false })], DevWalletBalances.prototype, "coins", void 0);
321
366
  __decorate([state()], DevWalletBalances.prototype, "_balances", void 0);
322
367
  __decorate([state()], DevWalletBalances.prototype, "_loading", void 0);
323
368
  __decorate([state()], DevWalletBalances.prototype, "_error", void 0);
369
+ __decorate([state()], DevWalletBalances.prototype, "_funding", void 0);
370
+ __decorate([state()], DevWalletBalances.prototype, "_fundError", void 0);
324
371
  DevWalletBalances = __decorate([customElement("dev-wallet-balances")], DevWalletBalances);
325
372
  //#endregion
326
373
  export { DevWalletBalances };
@@ -1 +1 @@
1
- {"version":3,"file":"dev-wallet-balances.mjs","names":["#fetchBalances","#lastCoinsRef","#coinIndex","#lastFetchedAddress","#lastFetchedClient","#fetchGeneration"],"sources":["../../src/ui/dev-wallet-balances.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { ClientWithCoreApi } from '@mysten/sui/client';\nimport { css, html, LitElement, nothing } from 'lit';\nimport { customElement, property, state } from 'lit/decorators.js';\n\nimport { sectionHeaderStyles, sharedStyles, stateStyles } from './styles.js';\nimport type { CoinManifestEntry, CoinRecord } from './utils.js';\nimport {\n\tformatAddress,\n\tformatCoinBalance,\n\tgetCoinSymbol,\n\tindexCoinsByType,\n\tisSuiCoinType,\n\tlookupCoinByType,\n} from './utils.js';\n\ninterface CoinBalance {\n\tcoinType: string;\n\tsymbol: string;\n\ttotalBalance: string;\n\tdecimals: number;\n}\n\n@customElement('dev-wallet-balances')\nexport class DevWalletBalances extends LitElement {\n\tstatic override styles = [\n\t\tsharedStyles,\n\t\tsectionHeaderStyles,\n\t\tstateStyles,\n\t\tcss`\n\t\t\t:host {\n\t\t\t\tdisplay: block;\n\t\t\t}\n\n\t\t\t.balance-list {\n\t\t\t\tdisplay: flex;\n\t\t\t\tflex-direction: column;\n\t\t\t\tgap: 2px;\n\t\t\t}\n\n\t\t\t.balance-item {\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tgap: 10px;\n\t\t\t\tpadding: 8px 2px;\n\t\t\t\tborder-radius: var(--dev-wallet-radius);\n\t\t\t\tbackground: transparent;\n\t\t\t\ttransition: background 120ms;\n\t\t\t}\n\n\t\t\t.balance-item:hover {\n\t\t\t\tbackground: var(--dev-wallet-bg-hover);\n\t\t\t}\n\n\t\t\t.balance-main {\n\t\t\t\tflex: 1;\n\t\t\t\tmin-width: 0;\n\t\t\t}\n\n\t\t\t.token-icon {\n\t\t\t\twidth: 28px;\n\t\t\t\theight: 28px;\n\t\t\t\tborder-radius: 50%;\n\t\t\t\tdisplay: inline-flex;\n\t\t\t\talign-items: center;\n\t\t\t\tjustify-content: center;\n\t\t\t\tflex-shrink: 0;\n\t\t\t\tbackground: linear-gradient(135deg, var(--dev-wallet-primary), var(--dev-wallet-teal));\n\t\t\t\tcolor: #fff;\n\t\t\t\tfont-family: var(--dev-wallet-font-mono);\n\t\t\t\tfont-size: 10px;\n\t\t\t\tfont-weight: var(--dev-wallet-font-weight-semibold);\n\t\t\t\tbox-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.15);\n\t\t\t}\n\n\t\t\t.balance-symbol {\n\t\t\t\tfont-size: 12px;\n\t\t\t\tfont-weight: var(--dev-wallet-font-weight-medium);\n\t\t\t\tcolor: var(--dev-wallet-foreground);\n\t\t\t}\n\n\t\t\t.balance-name {\n\t\t\t\tfont-size: 11px;\n\t\t\t\tcolor: var(--dev-wallet-text-3);\n\t\t\t\toverflow: hidden;\n\t\t\t\ttext-overflow: ellipsis;\n\t\t\t\twhite-space: nowrap;\n\t\t\t}\n\n\t\t\t.balance-amount-stack {\n\t\t\t\tdisplay: flex;\n\t\t\t\tflex-direction: column;\n\t\t\t\talign-items: flex-end;\n\t\t\t\tgap: 1px;\n\t\t\t\tfont-size: 12px;\n\t\t\t\tcolor: var(--dev-wallet-foreground);\n\t\t\t\tfont-family: var(--dev-wallet-font-mono);\n\t\t\t\tfont-variant-numeric: tabular-nums;\n\t\t\t}\n\n\t\t\t.balance-amount-symbol {\n\t\t\t\tfont-size: 10.5px;\n\t\t\t\tcolor: var(--dev-wallet-text-3);\n\t\t\t}\n\n\t\t\t.coins-header {\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tjustify-content: space-between;\n\t\t\t\tgap: 8px;\n\t\t\t}\n\n\t\t\t.coins-header .section-header {\n\t\t\t\tmargin-bottom: 0;\n\t\t\t}\n\n\t\t\t.manage-btn,\n\t\t\t.action-btn {\n\t\t\t\theight: 30px;\n\t\t\t\tpadding: 0 10px;\n\t\t\t\tborder-radius: var(--dev-wallet-radius);\n\t\t\t\tborder: 1px solid var(--dev-wallet-border);\n\t\t\t\tbackground: transparent;\n\t\t\t\tcolor: var(--dev-wallet-text-2);\n\t\t\t\tfont-size: 12px;\n\t\t\t\tfont-weight: var(--dev-wallet-font-weight-medium);\n\t\t\t}\n\n\t\t\t.manage-btn:hover,\n\t\t\t.action-btn:hover:not(:disabled) {\n\t\t\t\tbackground: var(--dev-wallet-bg-hover);\n\t\t\t\tborder-color: var(--dev-wallet-border-strong);\n\t\t\t\tcolor: var(--dev-wallet-foreground);\n\t\t\t}\n\n\t\t\t.manage-btn:disabled {\n\t\t\t\topacity: 0.48;\n\t\t\t\tcursor: not-allowed;\n\t\t\t}\n\n\t\t\t.balance-hero {\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tjustify-content: space-between;\n\t\t\t\tgap: 12px;\n\t\t\t\tmargin-bottom: 18px;\n\t\t\t\tpadding: 16px;\n\t\t\t\tborder: 1px solid var(--dev-wallet-border);\n\t\t\t\tborder-radius: var(--dev-wallet-radius-lg);\n\t\t\t\tbackground:\n\t\t\t\t\tradial-gradient(\n\t\t\t\t\t\tcircle at 20% 0%,\n\t\t\t\t\t\tcolor-mix(in srgb, var(--dev-wallet-primary) 18%, transparent),\n\t\t\t\t\t\ttransparent 42%\n\t\t\t\t\t),\n\t\t\t\t\tvar(--dev-wallet-surface);\n\t\t\t}\n\n\t\t\t.hero-copy {\n\t\t\t\tmin-width: 0;\n\t\t\t}\n\n\t\t\t.hero-balance {\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: baseline;\n\t\t\t\tgap: 8px;\n\t\t\t}\n\n\t\t\t.hero-value {\n\t\t\t\tfont-size: 30px;\n\t\t\t\tline-height: 1;\n\t\t\t\tfont-weight: var(--dev-wallet-font-weight-semibold);\n\t\t\t\tfont-variant-numeric: tabular-nums;\n\t\t\t\tletter-spacing: 0;\n\t\t\t}\n\n\t\t\t.hero-unit {\n\t\t\t\tfont-family: var(--dev-wallet-font-mono);\n\t\t\t\tfont-size: 13px;\n\t\t\t\tcolor: var(--dev-wallet-text-2);\n\t\t\t}\n\n\t\t\t.hero-note {\n\t\t\t\tmargin-top: 6px;\n\t\t\t\tfont-size: 12px;\n\t\t\t\tcolor: var(--dev-wallet-text-3);\n\t\t\t\toverflow: hidden;\n\t\t\t\ttext-overflow: ellipsis;\n\t\t\t\twhite-space: nowrap;\n\t\t\t}\n\n\t\t\t.empty-wallet {\n\t\t\t\ttext-align: left;\n\t\t\t\tpadding: 16px;\n\t\t\t}\n\n\t\t\t.empty-title {\n\t\t\t\tfont-size: 13px;\n\t\t\t\tfont-weight: var(--dev-wallet-font-weight-semibold);\n\t\t\t\tcolor: var(--dev-wallet-foreground);\n\t\t\t}\n\n\t\t\t.empty-copy {\n\t\t\t\tmargin-top: 6px;\n\t\t\t\tfont-size: 12px;\n\t\t\t\tline-height: 1.45;\n\t\t\t\tcolor: var(--dev-wallet-text-2);\n\t\t\t}\n\t\t`,\n\t];\n\n\t@property({ type: String })\n\taddress = '';\n\n\t@property({ attribute: false })\n\tclient: ClientWithCoreApi | null = null;\n\n\t@property({ type: String })\n\tnetwork = '';\n\n\t/** Optional pre-seeded coin metadata — pass the generated `coins`\n\t * constant from devstack codegen (`src/generated/coins.ts`) to skip\n\t * per-coin `getCoinMetadata` RPC waterfalls on UI load. Unknown coin\n\t * types fall through to an RPC fetch as before. */\n\t@property({ attribute: false })\n\tcoins: CoinRecord | null = null;\n\n\t@state()\n\tprivate _balances: CoinBalance[] = [];\n\n\t@state()\n\tprivate _loading = false;\n\n\t@state()\n\tprivate _error: string | null = null;\n\n\t#lastFetchedAddress = '';\n\t#lastFetchedClient: ClientWithCoreApi | null = null;\n\t#fetchGeneration = 0;\n\t#coinIndex: ReadonlyMap<string, CoinManifestEntry> = new Map();\n\t#lastCoinsRef: CoinRecord | null = null;\n\n\t/** Re-fetch balances for the current address/client. */\n\trefresh() {\n\t\tif (this.address && this.client) {\n\t\t\tthis.#fetchBalances();\n\t\t}\n\t}\n\n\toverride willUpdate(changedProperties: Map<string, unknown>) {\n\t\tif (changedProperties.has('coins') && this.coins !== this.#lastCoinsRef) {\n\t\t\tthis.#coinIndex = indexCoinsByType(this.coins);\n\t\t\tthis.#lastCoinsRef = this.coins;\n\t\t}\n\t\tif (\n\t\t\t(changedProperties.has('address') || changedProperties.has('client')) &&\n\t\t\tthis.address &&\n\t\t\tthis.client &&\n\t\t\t(this.address !== this.#lastFetchedAddress || this.client !== this.#lastFetchedClient)\n\t\t) {\n\t\t\tthis.#fetchBalances();\n\t\t}\n\t}\n\n\toverride render() {\n\t\tif (!this.address || !this.client) {\n\t\t\treturn nothing;\n\t\t}\n\n\t\tconst suiBalance = this._balances.find(\n\t\t\t(balance) => isSuiCoinType(balance.coinType) || balance.symbol === 'SUI',\n\t\t);\n\t\tconst formattedSui = suiBalance\n\t\t\t? formatCoinBalance(suiBalance.totalBalance, suiBalance.decimals)\n\t\t\t: '0.0000';\n\t\tconst networkLabel = this.network || 'active network';\n\n\t\treturn html`\n\t\t\t<div class=\"balance-hero\">\n\t\t\t\t<div class=\"hero-copy\">\n\t\t\t\t\t<div class=\"hero-balance\">\n\t\t\t\t\t\t<span class=\"hero-value\">${formattedSui}</span>\n\t\t\t\t\t\t<span class=\"hero-unit\">SUI</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"hero-note\">${formatAddress(this.address)} · ${networkLabel}</div>\n\t\t\t\t</div>\n\t\t\t\t<button class=\"action-btn\" @click=${this.refresh}>Refresh</button>\n\t\t\t</div>\n\t\t\t<div class=\"coins-header\">\n\t\t\t\t<h3 class=\"section-header\">Coins · ${this._balances.length}</h3>\n\t\t\t\t<button class=\"manage-btn\" disabled>Manage</button>\n\t\t\t</div>\n\t\t\t${this._loading\n\t\t\t\t? html`<div class=\"loading\" part=\"loading\" aria-live=\"polite\">Loading...</div>`\n\t\t\t\t: this._error\n\t\t\t\t\t? html`<div class=\"error-state\" part=\"error-message\" aria-live=\"polite\">\n\t\t\t\t\t\t\t${this._error}\n\t\t\t\t\t\t</div>`\n\t\t\t\t\t: this._balances.length === 0\n\t\t\t\t\t\t? html`<div class=\"empty-state empty-wallet\" part=\"empty-state\">\n\t\t\t\t\t\t\t\t<div class=\"empty-title\">No balances on ${networkLabel}</div>\n\t\t\t\t\t\t\t\t<div class=\"empty-copy\">\n\t\t\t\t\t\t\t\t\tFund ${formatAddress(this.address)} from your local faucet or devstack seed, then\n\t\t\t\t\t\t\t\t\trefresh balances here.\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>`\n\t\t\t\t\t\t: html`\n\t\t\t\t\t\t\t\t<div class=\"balance-list\" part=\"balance-list\">\n\t\t\t\t\t\t\t\t\t${this._balances.map((balance) => {\n\t\t\t\t\t\t\t\t\t\tconst seed = lookupCoinByType(this.#coinIndex, balance.coinType);\n\t\t\t\t\t\t\t\t\t\tconst name = seed?.displayName ?? balance.symbol;\n\t\t\t\t\t\t\t\t\t\treturn html`\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"balance-item\">\n\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"token-icon\">${balance.symbol.slice(0, 3)}</span>\n\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"balance-main\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"balance-symbol\">${balance.symbol}</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"balance-name\">${name}</div>\n\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"balance-amount-stack\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"balance-amount\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t${formatCoinBalance(balance.totalBalance, balance.decimals)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"balance-amount-symbol\">${balance.symbol}</span>\n\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t`;\n\t\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t`}\n\t\t`;\n\t}\n\n\tasync #fetchBalances() {\n\t\tthis.#lastFetchedAddress = this.address;\n\t\tthis.#lastFetchedClient = this.client;\n\t\tconst generation = ++this.#fetchGeneration;\n\t\tthis._loading = true;\n\t\tthis._error = null;\n\n\t\ttry {\n\t\t\tconst { balances } = await this.client!.core.listBalances({ owner: this.address });\n\n\t\t\tif (generation !== this.#fetchGeneration) return;\n\n\t\t\t// Pre-seed metadata from the generated `coins` record. Each\n\t\t\t// balance with a known coinType skips the per-coin RPC entirely;\n\t\t\t// only the (typically empty) remainder hits the network.\n\t\t\tconst preseeded = balances.map((b) => lookupCoinByType(this.#coinIndex, b.coinType));\n\t\t\tconst metadataResults = await Promise.all(\n\t\t\t\tbalances.map((b, i) => {\n\t\t\t\t\tif (preseeded[i] !== undefined) return null;\n\t\t\t\t\treturn this.client!.core.getCoinMetadata({ coinType: b.coinType }).catch(() => null);\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tif (generation !== this.#fetchGeneration) return;\n\t\t\tthis._balances = balances.map((b, i): CoinBalance => {\n\t\t\t\tconst seed = preseeded[i];\n\t\t\t\tif (seed !== undefined) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcoinType: b.coinType,\n\t\t\t\t\t\tsymbol: seed.symbol ?? getCoinSymbol(b.coinType),\n\t\t\t\t\t\ttotalBalance: b.balance,\n\t\t\t\t\t\tdecimals: seed.decimals,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tcoinType: b.coinType,\n\t\t\t\t\tsymbol: metadataResults[i]?.coinMetadata?.symbol ?? getCoinSymbol(b.coinType),\n\t\t\t\t\ttotalBalance: b.balance,\n\t\t\t\t\tdecimals: metadataResults[i]?.coinMetadata?.decimals ?? 0,\n\t\t\t\t};\n\t\t\t});\n\t\t} catch {\n\t\t\tif (generation !== this.#fetchGeneration) return;\n\t\t\tthis._error = 'Failed to load balances';\n\t\t\tthis._balances = [];\n\t\t} finally {\n\t\t\tif (generation === this.#fetchGeneration) {\n\t\t\t\tthis._loading = false;\n\t\t\t}\n\t\t}\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'dev-wallet-balances': DevWalletBalances;\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"dev-wallet-balances.mjs","names":["#fetchBalances","#requestFromFaucet","#lastCoinsRef","#coinIndex","#lastFetchedAddress","#lastFetchedClient","#fetchGeneration"],"sources":["../../src/ui/dev-wallet-balances.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { ClientWithCoreApi } from '@mysten/sui/client';\nimport { requestSuiFromFaucetV2 } from '@mysten/sui/faucet';\nimport { css, html, LitElement, nothing } from 'lit';\nimport { customElement, property, state } from 'lit/decorators.js';\n\nimport { sectionHeaderStyles, sharedStyles, stateStyles } from './styles.js';\nimport type { CoinManifestEntry, CoinRecord } from './utils.js';\nimport {\n\tformatAddress,\n\tformatCoinBalance,\n\tgetCoinSymbol,\n\tindexCoinsByType,\n\tisSuiCoinType,\n\tlookupCoinByType,\n} from './utils.js';\n\ninterface CoinBalance {\n\tcoinType: string;\n\tsymbol: string;\n\ttotalBalance: string;\n\tdecimals: number;\n}\n\n@customElement('dev-wallet-balances')\nexport class DevWalletBalances extends LitElement {\n\tstatic override styles = [\n\t\tsharedStyles,\n\t\tsectionHeaderStyles,\n\t\tstateStyles,\n\t\tcss`\n\t\t\t:host {\n\t\t\t\tdisplay: block;\n\t\t\t}\n\n\t\t\t.balance-list {\n\t\t\t\tdisplay: flex;\n\t\t\t\tflex-direction: column;\n\t\t\t\tgap: 2px;\n\t\t\t}\n\n\t\t\t.balance-item {\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tgap: 10px;\n\t\t\t\tpadding: 8px 2px;\n\t\t\t\tborder-radius: var(--dev-wallet-radius);\n\t\t\t\tbackground: transparent;\n\t\t\t\ttransition: background 120ms;\n\t\t\t}\n\n\t\t\t.balance-item:hover {\n\t\t\t\tbackground: var(--dev-wallet-bg-hover);\n\t\t\t}\n\n\t\t\t.balance-main {\n\t\t\t\tflex: 1;\n\t\t\t\tmin-width: 0;\n\t\t\t}\n\n\t\t\t.token-icon {\n\t\t\t\twidth: 28px;\n\t\t\t\theight: 28px;\n\t\t\t\tborder-radius: 50%;\n\t\t\t\tdisplay: inline-flex;\n\t\t\t\talign-items: center;\n\t\t\t\tjustify-content: center;\n\t\t\t\tflex-shrink: 0;\n\t\t\t\tbackground: linear-gradient(135deg, var(--dev-wallet-primary), var(--dev-wallet-teal));\n\t\t\t\tcolor: #fff;\n\t\t\t\tfont-family: var(--dev-wallet-font-mono);\n\t\t\t\tfont-size: 10px;\n\t\t\t\tfont-weight: var(--dev-wallet-font-weight-semibold);\n\t\t\t\tbox-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.15);\n\t\t\t}\n\n\t\t\t.balance-symbol {\n\t\t\t\tfont-size: 12px;\n\t\t\t\tfont-weight: var(--dev-wallet-font-weight-medium);\n\t\t\t\tcolor: var(--dev-wallet-foreground);\n\t\t\t}\n\n\t\t\t.balance-name {\n\t\t\t\tfont-size: 11px;\n\t\t\t\tcolor: var(--dev-wallet-text-3);\n\t\t\t\toverflow: hidden;\n\t\t\t\ttext-overflow: ellipsis;\n\t\t\t\twhite-space: nowrap;\n\t\t\t}\n\n\t\t\t.balance-amount-stack {\n\t\t\t\tdisplay: flex;\n\t\t\t\tflex-direction: column;\n\t\t\t\talign-items: flex-end;\n\t\t\t\tgap: 1px;\n\t\t\t\tfont-size: 12px;\n\t\t\t\tcolor: var(--dev-wallet-foreground);\n\t\t\t\tfont-family: var(--dev-wallet-font-mono);\n\t\t\t\tfont-variant-numeric: tabular-nums;\n\t\t\t}\n\n\t\t\t.balance-amount-symbol {\n\t\t\t\tfont-size: 10.5px;\n\t\t\t\tcolor: var(--dev-wallet-text-3);\n\t\t\t}\n\n\t\t\t.coins-header {\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tjustify-content: space-between;\n\t\t\t\tgap: 8px;\n\t\t\t}\n\n\t\t\t.coins-header .section-header {\n\t\t\t\tmargin-bottom: 0;\n\t\t\t}\n\n\t\t\t.manage-btn,\n\t\t\t.action-btn {\n\t\t\t\theight: 30px;\n\t\t\t\tpadding: 0 10px;\n\t\t\t\tborder-radius: var(--dev-wallet-radius);\n\t\t\t\tborder: 1px solid var(--dev-wallet-border);\n\t\t\t\tbackground: transparent;\n\t\t\t\tcolor: var(--dev-wallet-text-2);\n\t\t\t\tfont-size: 12px;\n\t\t\t\tfont-weight: var(--dev-wallet-font-weight-medium);\n\t\t\t}\n\n\t\t\t.manage-btn:hover,\n\t\t\t.action-btn:hover:not(:disabled) {\n\t\t\t\tbackground: var(--dev-wallet-bg-hover);\n\t\t\t\tborder-color: var(--dev-wallet-border-strong);\n\t\t\t\tcolor: var(--dev-wallet-foreground);\n\t\t\t}\n\n\t\t\t.manage-btn:disabled {\n\t\t\t\topacity: 0.48;\n\t\t\t\tcursor: not-allowed;\n\t\t\t}\n\n\t\t\t.balance-hero {\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tjustify-content: space-between;\n\t\t\t\tgap: 12px;\n\t\t\t\tmargin-bottom: 18px;\n\t\t\t\tpadding: 16px;\n\t\t\t\tborder: 1px solid var(--dev-wallet-border);\n\t\t\t\tborder-radius: var(--dev-wallet-radius-lg);\n\t\t\t\tbackground:\n\t\t\t\t\tradial-gradient(\n\t\t\t\t\t\tcircle at 20% 0%,\n\t\t\t\t\t\tcolor-mix(in srgb, var(--dev-wallet-primary) 18%, transparent),\n\t\t\t\t\t\ttransparent 42%\n\t\t\t\t\t),\n\t\t\t\t\tvar(--dev-wallet-surface);\n\t\t\t}\n\n\t\t\t.hero-copy {\n\t\t\t\tmin-width: 0;\n\t\t\t}\n\n\t\t\t.hero-actions {\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tgap: 6px;\n\t\t\t\tflex-shrink: 0;\n\t\t\t}\n\n\t\t\t.hero-balance {\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: baseline;\n\t\t\t\tgap: 8px;\n\t\t\t}\n\n\t\t\t.hero-value {\n\t\t\t\tfont-size: 30px;\n\t\t\t\tline-height: 1;\n\t\t\t\tfont-weight: var(--dev-wallet-font-weight-semibold);\n\t\t\t\tfont-variant-numeric: tabular-nums;\n\t\t\t\tletter-spacing: 0;\n\t\t\t}\n\n\t\t\t.hero-unit {\n\t\t\t\tfont-family: var(--dev-wallet-font-mono);\n\t\t\t\tfont-size: 13px;\n\t\t\t\tcolor: var(--dev-wallet-text-2);\n\t\t\t}\n\n\t\t\t.hero-note {\n\t\t\t\tmargin-top: 6px;\n\t\t\t\tfont-size: 12px;\n\t\t\t\tcolor: var(--dev-wallet-text-3);\n\t\t\t\toverflow: hidden;\n\t\t\t\ttext-overflow: ellipsis;\n\t\t\t\twhite-space: nowrap;\n\t\t\t}\n\n\t\t\t.empty-wallet {\n\t\t\t\ttext-align: left;\n\t\t\t\tpadding: 16px;\n\t\t\t}\n\n\t\t\t.empty-title {\n\t\t\t\tfont-size: 13px;\n\t\t\t\tfont-weight: var(--dev-wallet-font-weight-semibold);\n\t\t\t\tcolor: var(--dev-wallet-foreground);\n\t\t\t}\n\n\t\t\t.empty-copy {\n\t\t\t\tmargin-top: 6px;\n\t\t\t\tfont-size: 12px;\n\t\t\t\tline-height: 1.45;\n\t\t\t\tcolor: var(--dev-wallet-text-2);\n\t\t\t}\n\t\t`,\n\t];\n\n\t@property({ type: String })\n\taddress = '';\n\n\t@property({ attribute: false })\n\tclient: ClientWithCoreApi | null = null;\n\n\t@property({ type: String })\n\tnetwork = '';\n\n\t/** Faucet endpoint for the active network (the wallet's `activeFaucet`),\n\t * or `null` when the active network has no faucet (live mainnet, fork\n\t * stacks). When set, a \"Request from faucet\" button funds the active\n\t * address against this endpoint and refreshes balances. */\n\t@property({ attribute: false })\n\tfaucetHost: string | null = null;\n\n\t/** Optional pre-seeded coin metadata — pass the generated `coins`\n\t * constant from devstack codegen (`src/generated/coins.ts`) to skip\n\t * per-coin `getCoinMetadata` RPC waterfalls on UI load. Unknown coin\n\t * types fall through to an RPC fetch as before. */\n\t@property({ attribute: false })\n\tcoins: CoinRecord | null = null;\n\n\t@state()\n\tprivate _balances: CoinBalance[] = [];\n\n\t@state()\n\tprivate _loading = false;\n\n\t@state()\n\tprivate _error: string | null = null;\n\n\t@state()\n\tprivate _funding = false;\n\n\t@state()\n\tprivate _fundError: string | null = null;\n\n\t#lastFetchedAddress = '';\n\t#lastFetchedClient: ClientWithCoreApi | null = null;\n\t#fetchGeneration = 0;\n\t#coinIndex: ReadonlyMap<string, CoinManifestEntry> = new Map();\n\t#lastCoinsRef: CoinRecord | null = null;\n\n\t/** Re-fetch balances for the current address/client. */\n\trefresh() {\n\t\tif (this.address && this.client) {\n\t\t\tthis.#fetchBalances();\n\t\t}\n\t}\n\n\t/** Request SUI from the active network's faucet for the active address,\n\t * then refresh balances. No-op when no `faucetHost` is configured (live\n\t * mainnet / fork stacks have none). */\n\tasync #requestFromFaucet() {\n\t\tif (!this.faucetHost || !this.address || this._funding) return;\n\t\tthis._funding = true;\n\t\tthis._fundError = null;\n\t\ttry {\n\t\t\tawait requestSuiFromFaucetV2({ host: this.faucetHost, recipient: this.address });\n\t\t\tthis.refresh();\n\t\t} catch (error) {\n\t\t\tthis._fundError =\n\t\t\t\terror instanceof Error ? error.message : 'Faucet request failed. Try again.';\n\t\t} finally {\n\t\t\tthis._funding = false;\n\t\t}\n\t}\n\n\toverride willUpdate(changedProperties: Map<string, unknown>) {\n\t\tif (changedProperties.has('coins') && this.coins !== this.#lastCoinsRef) {\n\t\t\tthis.#coinIndex = indexCoinsByType(this.coins);\n\t\t\tthis.#lastCoinsRef = this.coins;\n\t\t}\n\t\tif (\n\t\t\t(changedProperties.has('address') || changedProperties.has('client')) &&\n\t\t\tthis.address &&\n\t\t\tthis.client &&\n\t\t\t(this.address !== this.#lastFetchedAddress || this.client !== this.#lastFetchedClient)\n\t\t) {\n\t\t\tthis.#fetchBalances();\n\t\t}\n\t}\n\n\toverride render() {\n\t\tif (!this.address || !this.client) {\n\t\t\treturn nothing;\n\t\t}\n\n\t\tconst suiBalance = this._balances.find(\n\t\t\t(balance) => isSuiCoinType(balance.coinType) || balance.symbol === 'SUI',\n\t\t);\n\t\tconst formattedSui = suiBalance\n\t\t\t? formatCoinBalance(suiBalance.totalBalance, suiBalance.decimals)\n\t\t\t: '0.0000';\n\t\tconst networkLabel = this.network || 'active network';\n\n\t\treturn html`\n\t\t\t<div class=\"balance-hero\">\n\t\t\t\t<div class=\"hero-copy\">\n\t\t\t\t\t<div class=\"hero-balance\">\n\t\t\t\t\t\t<span class=\"hero-value\">${formattedSui}</span>\n\t\t\t\t\t\t<span class=\"hero-unit\">SUI</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"hero-note\">${formatAddress(this.address)} · ${networkLabel}</div>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"hero-actions\">\n\t\t\t\t\t${this.faucetHost\n\t\t\t\t\t\t? html`<button\n\t\t\t\t\t\t\t\tclass=\"action-btn\"\n\t\t\t\t\t\t\t\tpart=\"faucet-button\"\n\t\t\t\t\t\t\t\t?disabled=${this._funding}\n\t\t\t\t\t\t\t\t@click=${this.#requestFromFaucet}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t${this._funding ? 'Funding...' : 'Faucet'}\n\t\t\t\t\t\t\t</button>`\n\t\t\t\t\t\t: nothing}\n\t\t\t\t\t<button class=\"action-btn\" @click=${this.refresh}>Refresh</button>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t${this._fundError\n\t\t\t\t? html`<div class=\"error-state\" part=\"faucet-error-message\" aria-live=\"polite\">\n\t\t\t\t\t\t${this._fundError}\n\t\t\t\t\t</div>`\n\t\t\t\t: nothing}\n\t\t\t<div class=\"coins-header\">\n\t\t\t\t<h3 class=\"section-header\">Coins · ${this._balances.length}</h3>\n\t\t\t\t<button class=\"manage-btn\" disabled>Manage</button>\n\t\t\t</div>\n\t\t\t${this._loading\n\t\t\t\t? html`<div class=\"loading\" part=\"loading\" aria-live=\"polite\">Loading...</div>`\n\t\t\t\t: this._error\n\t\t\t\t\t? html`<div class=\"error-state\" part=\"error-message\" aria-live=\"polite\">\n\t\t\t\t\t\t\t${this._error}\n\t\t\t\t\t\t</div>`\n\t\t\t\t\t: this._balances.length === 0\n\t\t\t\t\t\t? html`<div class=\"empty-state empty-wallet\" part=\"empty-state\">\n\t\t\t\t\t\t\t\t<div class=\"empty-title\">No balances on ${networkLabel}</div>\n\t\t\t\t\t\t\t\t<div class=\"empty-copy\">\n\t\t\t\t\t\t\t\t\t${this.faucetHost\n\t\t\t\t\t\t\t\t\t\t? html`Use the Faucet button above to fund ${formatAddress(this.address)} on\n\t\t\t\t\t\t\t\t\t\t\t${networkLabel}, then refresh.`\n\t\t\t\t\t\t\t\t\t\t: html`Fund ${formatAddress(this.address)} from your local faucet or devstack\n\t\t\t\t\t\t\t\t\t\t\tseed, then refresh balances here.`}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>`\n\t\t\t\t\t\t: html`\n\t\t\t\t\t\t\t\t<div class=\"balance-list\" part=\"balance-list\">\n\t\t\t\t\t\t\t\t\t${this._balances.map((balance) => {\n\t\t\t\t\t\t\t\t\t\tconst seed = lookupCoinByType(this.#coinIndex, balance.coinType);\n\t\t\t\t\t\t\t\t\t\tconst name = seed?.displayName ?? balance.symbol;\n\t\t\t\t\t\t\t\t\t\treturn html`\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"balance-item\">\n\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"token-icon\">${balance.symbol.slice(0, 3)}</span>\n\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"balance-main\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"balance-symbol\">${balance.symbol}</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"balance-name\">${name}</div>\n\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"balance-amount-stack\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"balance-amount\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t${formatCoinBalance(balance.totalBalance, balance.decimals)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"balance-amount-symbol\">${balance.symbol}</span>\n\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t`;\n\t\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t`}\n\t\t`;\n\t}\n\n\tasync #fetchBalances() {\n\t\tthis.#lastFetchedAddress = this.address;\n\t\tthis.#lastFetchedClient = this.client;\n\t\tconst generation = ++this.#fetchGeneration;\n\t\tthis._loading = true;\n\t\tthis._error = null;\n\n\t\ttry {\n\t\t\tconst { balances } = await this.client!.core.listBalances({ owner: this.address });\n\n\t\t\tif (generation !== this.#fetchGeneration) return;\n\n\t\t\t// Pre-seed metadata from the generated `coins` record. Each\n\t\t\t// balance with a known coinType skips the per-coin RPC entirely;\n\t\t\t// only the (typically empty) remainder hits the network.\n\t\t\tconst preseeded = balances.map((b) => lookupCoinByType(this.#coinIndex, b.coinType));\n\t\t\tconst metadataResults = await Promise.all(\n\t\t\t\tbalances.map((b, i) => {\n\t\t\t\t\tif (preseeded[i] !== undefined) return null;\n\t\t\t\t\treturn this.client!.core.getCoinMetadata({ coinType: b.coinType }).catch(() => null);\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tif (generation !== this.#fetchGeneration) return;\n\t\t\tthis._balances = balances.map((b, i): CoinBalance => {\n\t\t\t\tconst seed = preseeded[i];\n\t\t\t\tif (seed !== undefined) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcoinType: b.coinType,\n\t\t\t\t\t\tsymbol: seed.symbol ?? getCoinSymbol(b.coinType),\n\t\t\t\t\t\ttotalBalance: b.balance,\n\t\t\t\t\t\tdecimals: seed.decimals,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tcoinType: b.coinType,\n\t\t\t\t\tsymbol: metadataResults[i]?.coinMetadata?.symbol ?? getCoinSymbol(b.coinType),\n\t\t\t\t\ttotalBalance: b.balance,\n\t\t\t\t\tdecimals: metadataResults[i]?.coinMetadata?.decimals ?? 0,\n\t\t\t\t};\n\t\t\t});\n\t\t} catch {\n\t\t\tif (generation !== this.#fetchGeneration) return;\n\t\t\tthis._error = 'Failed to load balances';\n\t\t\tthis._balances = [];\n\t\t} finally {\n\t\t\tif (generation === this.#fetchGeneration) {\n\t\t\t\tthis._loading = false;\n\t\t\t}\n\t\t}\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'dev-wallet-balances': DevWalletBalances;\n\t}\n}\n"],"mappings":"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"}
@@ -161,10 +161,11 @@ var WalletController = class {
161
161
  ${this.activeAddress && this.#wallet ? html`
162
162
  <div class="section">
163
163
  <dev-wallet-balances
164
- exportparts="balance-list, loading: balances-loading, error-message: balances-error-message, empty-state: balances-empty-state"
164
+ exportparts="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"
165
165
  .address=${this.activeAddress}
166
166
  .client=${this.getActiveClient()}
167
167
  .network=${this.#wallet.activeNetwork}
168
+ .faucetHost=${this.#wallet.activeFaucet}
168
169
  .coins=${this.coins}
169
170
  ></dev-wallet-balances>
170
171
  </div>
@@ -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\"\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.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","#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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"}
@@ -48,6 +48,12 @@ interface DevWalletConfig {
48
48
  adapters: SignerAdapter[];
49
49
  /** Network URLs by name (network → gRPC endpoint). Defaults to devnet, testnet, and localnet. */
50
50
  networks?: Record<string, string>;
51
+ /** Optional per-network faucet endpoints (network → faucet URL). The
52
+ * active network's faucet is resolvable via {@link DevWallet.activeFaucet}
53
+ * / {@link DevWallet.getFaucet}, so a fund flow funds the SELECTED
54
+ * network's account. Networks without a faucet (live mainnet, fork
55
+ * stacks) simply omit a key. */
56
+ faucets?: Record<string, string>;
51
57
  /** Display name for the wallet. Defaults to 'Dev Wallet'. */
52
58
  name?: string;
53
59
  icon?: WalletIcon;
@@ -78,7 +84,15 @@ declare class DevWallet implements Wallet {
78
84
  get version(): "1.0.0";
79
85
  get name(): string;
80
86
  get icon(): `data:image/svg+xml;base64,${string}` | `data:image/webp;base64,${string}` | `data:image/png;base64,${string}` | `data:image/gif;base64,${string}`;
81
- get chains(): readonly ["sui:devnet", "sui:testnet", "sui:localnet", "sui:mainnet"];
87
+ /**
88
+ * Wallet-standard chains this wallet advertises. Reflects the CONFIGURED
89
+ * networks — each network name is advertised as `sui:<name>` so dApp Kit's
90
+ * chain-gated paths work for fork networks (`testnet-fork`, …) and custom
91
+ * network names, not just the fixed standard set. Unioned with the standard
92
+ * Sui chains for safety, de-duplicated, with the standard chains first and
93
+ * configured-only chains appended in network-map order.
94
+ */
95
+ get chains(): `sui:${string}`[];
82
96
  get accounts(): readonly ReadonlyWalletAccount[];
83
97
  /**
84
98
  * Narrow the accounts this wallet exposes to dApps (via the
@@ -101,12 +115,22 @@ declare class DevWallet implements Wallet {
101
115
  get clients(): Record<string, ClientWithCoreApi>;
102
116
  getClient(network: string): ClientWithCoreApi;
103
117
  get networkUrls(): Record<string, string>;
118
+ /** Per-network faucet endpoints (network → faucet URL). Networks without
119
+ * a faucet are absent from the map. */
120
+ get faucetUrls(): Record<string, string>;
121
+ /** Faucet endpoint for the named network, or `null` when that network has
122
+ * no faucet configured. */
123
+ getFaucet(network: string): string | null;
124
+ /** Faucet endpoint for the currently-active network, or `null` when the
125
+ * active network has no faucet (live mainnet, fork stacks). Drives a
126
+ * fund flow against the SELECTED network. */
127
+ get activeFaucet(): string | null;
104
128
  get activeNetwork(): string;
105
129
  get activeClient(): ClientWithCoreApi | null;
106
130
  get availableNetworks(): string[];
107
131
  /** Switch the active network. Emits a `change` event. */
108
132
  setActiveNetwork(network: string): void;
109
- addNetwork(name: string, url: string): void;
133
+ addNetwork(name: string, url: string, faucet?: string | null): void;
110
134
  removeNetwork(name: string): void;
111
135
  /** Register this wallet with the wallet-standard registry. Returns an unregister function. */
112
136
  register(): () => void;
@@ -1 +1 @@
1
- {"version":3,"file":"dev-wallet.d.mts","names":[],"sources":["../../src/wallet/dev-wallet.ts"],"mappings":";;;;;;;AAiCA;;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;EAyBA,IAAA;EACA,IAAA,GAAO,UAAA;EA1BG;EA4BV,aAAA;EA3BgB;;;AAAK;EAgCrB,WAAA,GAAc,iBAAA;EA5BkB;EA8BhC,WAAA;EA9BmC;EAgCnC,aAAA,IAAiB,OAAA,UAAiB,GAAA,aAAgB,iBAAA;EA1BvC;;;;EA+BX,eAAA;AAAA;;;;;cAOY,SAAA,YAAqB,MAAA;EAAA;cA2BrB,MAAA,EAAQ,eAAA;EAAA,IA2BhB,OAAA;EAAA,IAIA,IAAA;EAAA,IAIA,IAAA;EAAA,IAIA,MAAA;EAAA,IAIA,QAAA,aAAqB,qBAAA;EAnGT;;;;;;;;;;;EAkHhB,kBAAA,CAAmB,OAAA;EAAA,IAwBf,QAAA,IAAY,sBAAA,GACf,qBAAA,GACA,yBAAA,GACA,WAAA;EA1IU;EAAA,IAwKP,cAAA,IAAkB,qBAAA;EAAA,IAIlB,QAAA,aAAqB,aAAA;EAIzB,oBAAA,CAAqB,OAAA,WAAkB,aAAA;EA3KvC;EAAA,IAgLI,OAAA,IAAW,MAAA,SAAe,iBAAA;EAO9B,SAAA,CAAU,OAAA,WAAkB,iBAAA;EAAA,IAIxB,WAAA,IAAe,MAAA;EAAA,IAIf,aAAA;EAAA,IAIA,YAAA,IAAgB,iBAAA;EAAA,IAKhB,iBAAA;EA/L8C;EAoMlD,gBAAA,CAAiB,OAAA;EAUjB,UAAA,CAAW,IAAA,UAAc,GAAA;EAgBzB,aAAA,CAAc,IAAA;EAlNF;EA6NZ,QAAA;;EAWA,OAAA;EAlKyB;EA0LnB,cAAA,IAAkB,OAAA;EAuBxB,aAAA,CAAc,MAAA;EAxKb;EAmLD,eAAA,CAAgB,QAAA,GAAW,OAAA,EAAS,qBAAA;EApJd;EAAA,IA4JlB,cAAA,IAAkB,qBAAA;EApJiB;EAyJvC,cAAA,CAAe,iBAAA;EAgBf,aAAA,CAAc,MAAA;EA7Jc;EAwK5B,eAAA,CAAgB,QAAA,GAAW,OAAA,EAAS,qBAAA;EA5JhB;;;;;;EAuLpB,eAAA,CAAgB,QAAA;AAAA"}
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,8 +1,9 @@
1
+ import { chainsForNetworks } from "../adapters/build-managed-account.mjs";
1
2
  import { getNetworkFromChain } from "./constants.mjs";
2
3
  import { executeSigning } from "./signing.mjs";
3
4
  import { SuiGrpcClient } from "@mysten/sui/grpc";
4
5
  import { mitt } from "@mysten/utils";
5
- import { SUI_CHAINS, getWallets } from "@mysten/wallet-standard";
6
+ import { getWallets } from "@mysten/wallet-standard";
6
7
  //#region src/wallet/dev-wallet.ts
7
8
  const DEFAULT_WALLET_NAME = "Dev Wallet";
8
9
  const NETWORK_STORAGE_KEY = "dev-wallet:networks";
@@ -21,6 +22,7 @@ const DEFAULT_NETWORK_URLS = {
21
22
  var DevWallet = class {
22
23
  #adapters;
23
24
  #networkUrls;
25
+ #faucetUrls;
24
26
  #clients;
25
27
  #activeNetwork;
26
28
  #name;
@@ -49,6 +51,7 @@ var DevWallet = class {
49
51
  this.#adapters = config.adapters;
50
52
  this.#persistNetworks = config.persistNetworks ?? false;
51
53
  this.#networkUrls = this.#loadNetworkUrls(config.networks ?? DEFAULT_NETWORK_URLS);
54
+ this.#faucetUrls = { ...config.faucets };
52
55
  this.#clients = {};
53
56
  this.#activeNetwork = config.activeNetwork ?? Object.keys(this.#networkUrls)[0] ?? "";
54
57
  this.#name = config.name ?? DEFAULT_WALLET_NAME;
@@ -78,8 +81,16 @@ var DevWallet = class {
78
81
  get icon() {
79
82
  return this.#icon;
80
83
  }
84
+ /**
85
+ * Wallet-standard chains this wallet advertises. Reflects the CONFIGURED
86
+ * networks — each network name is advertised as `sui:<name>` so dApp Kit's
87
+ * chain-gated paths work for fork networks (`testnet-fork`, …) and custom
88
+ * network names, not just the fixed standard set. Unioned with the standard
89
+ * Sui chains for safety, de-duplicated, with the standard chains first and
90
+ * configured-only chains appended in network-map order.
91
+ */
81
92
  get chains() {
82
- return SUI_CHAINS;
93
+ return chainsForNetworks(Object.keys(this.#networkUrls));
83
94
  }
84
95
  get accounts() {
85
96
  return this.#exposedAccounts();
@@ -160,6 +171,22 @@ var DevWallet = class {
160
171
  get networkUrls() {
161
172
  return { ...this.#networkUrls };
162
173
  }
174
+ /** Per-network faucet endpoints (network → faucet URL). Networks without
175
+ * a faucet are absent from the map. */
176
+ get faucetUrls() {
177
+ return { ...this.#faucetUrls };
178
+ }
179
+ /** Faucet endpoint for the named network, or `null` when that network has
180
+ * no faucet configured. */
181
+ getFaucet(network) {
182
+ return this.#faucetUrls[network] ?? null;
183
+ }
184
+ /** Faucet endpoint for the currently-active network, or `null` when the
185
+ * active network has no faucet (live mainnet, fork stacks). Drives a
186
+ * fund flow against the SELECTED network. */
187
+ get activeFaucet() {
188
+ return this.getFaucet(this.#activeNetwork);
189
+ }
163
190
  get activeNetwork() {
164
191
  return this.#activeNetwork;
165
192
  }
@@ -174,9 +201,9 @@ var DevWallet = class {
174
201
  setActiveNetwork(network) {
175
202
  if (!this.#networkUrls[network]) throw new Error(`No client for network "${network}". Available: ${this.availableNetworks.join(", ")}`);
176
203
  this.#activeNetwork = network;
177
- this.#events.emit("change", { accounts: this.#accounts });
204
+ this.#events.emit("change", { accounts: this.#exposedAccounts() });
178
205
  }
179
- addNetwork(name, url) {
206
+ addNetwork(name, url, faucet) {
180
207
  let parsed;
181
208
  try {
182
209
  parsed = new URL(url);
@@ -186,15 +213,17 @@ var DevWallet = class {
186
213
  if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw new Error(`Invalid URL: must use http or https (got ${parsed.protocol})`);
187
214
  this.#networkUrls[name] = url;
188
215
  this.#clients[name] = this.#clientFactory(name, url);
216
+ if (faucet !== void 0 && faucet !== null) this.#faucetUrls[name] = faucet;
189
217
  this.#saveNetworkUrls();
190
- this.#events.emit("change", { accounts: this.#accounts });
218
+ this.#events.emit("change", { accounts: this.#exposedAccounts() });
191
219
  }
192
220
  removeNetwork(name) {
193
221
  delete this.#networkUrls[name];
222
+ delete this.#faucetUrls[name];
194
223
  delete this.#clients[name];
195
224
  if (this.#activeNetwork === name) this.#activeNetwork = Object.keys(this.#networkUrls)[0] ?? "";
196
225
  this.#saveNetworkUrls();
197
- this.#events.emit("change", { accounts: this.#accounts });
226
+ this.#events.emit("change", { accounts: this.#exposedAccounts() });
198
227
  }
199
228
  /** Register this wallet with the wallet-standard registry. Returns an unregister function. */
200
229
  register() {