kira-wallet 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -13
- package/THIRD_PARTY_NOTICES.md +7 -0
- package/docs/CLI.md +31 -13
- package/docs/KIRA_DISCOVERY_ACCEPTANCE.md +95 -0
- package/docs/RELEASE.md +54 -12
- package/kira_cli.py +25 -10
- package/kira_jobs.py +1 -1
- package/onchain.cjs +6 -1
- package/package.json +3 -2
- package/pipeline-onchain.cjs +19 -2
- package/viewer/model.py +10 -0
- package/viewer/server.py +1 -1
- package/viewer/static/app.js +30 -6
- package/viewer/static/index.html +6 -3
- package/viewer/static/jobs.js +9 -2
- package/viewer/static/ows-logo.svg +24 -0
- package/viewer/static/report.css +4 -0
- package/viewer/static/report.js +2 -2
- package/viewer/static/style.css +2 -0
- package/viewer/static/watching.js +5 -1
- package/viewer/static/workspace-model.js +22 -1
- package/viewer/static/workspace.css +7 -0
- package/wallet.py +17 -5
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="https://raw.githubusercontent.com/realproject7/kira-wallet/v0.1.
|
|
2
|
+
<img src="https://raw.githubusercontent.com/realproject7/kira-wallet/v0.1.2/viewer/static/kira-explain.png" width="140" alt="Kira, your wallet research partner">
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
<h1 align="center">Kira Wallet</h1>
|
|
@@ -15,31 +15,40 @@ what you hold across supported EVM chains, and ask Kira to research your tokens
|
|
|
15
15
|
or refresh your holdings. Kira runs on your computer and uses your existing
|
|
16
16
|
Codex or Claude Code account.
|
|
17
17
|
|
|
18
|
-

|
|
19
19
|
|
|
20
20
|
## Get started
|
|
21
21
|
|
|
22
22
|
**Requires Node.js 22+ and Python 3.11+. macOS is verified.** The research and
|
|
23
23
|
background-process features require a POSIX environment.
|
|
24
24
|
|
|
25
|
-
Install
|
|
25
|
+
Install Kira:
|
|
26
26
|
|
|
27
27
|
```sh
|
|
28
|
-
npm install -g
|
|
28
|
+
npm install -g kira-wallet
|
|
29
29
|
kira setup
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
Open the local app at [127.0.0.1:8787](http://127.0.0.1:8787).
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
The screenshots and features below show **0.1.1**, available from the GitHub
|
|
36
|
-
release above. npm publication is pending.
|
|
34
|
+
After your first setup, open Kira with:
|
|
37
35
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
```sh
|
|
37
|
+
kira start
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
It starts your local app and opens the browser. Use `kira stop` to stop it.
|
|
41
|
+
For a viewer without wallet or research actions, use `kira start --read-only`.
|
|
42
|
+
|
|
43
|
+
Prefer an archive? [GitHub releases](https://github.com/realproject7/kira-wallet/releases/tag/v0.1.2)
|
|
44
|
+
include the same package and its checksum.
|
|
45
|
+
|
|
46
|
+
1. **Set up discovery.** In **Workspace settings**, connect an indexer for broad
|
|
41
47
|
ERC20 discovery. Public RPC alone has limited discovery; incomplete coverage
|
|
42
|
-
stays visible.
|
|
48
|
+
stays visible. An AI or browser wallet connection does not enable an indexer.
|
|
49
|
+
2. **Add a watching wallet.** Paste a public address and give it a name. Add more
|
|
50
|
+
wallets whenever you need them. Without discovery, the form explicitly
|
|
51
|
+
offers limited research of native and Mint Club assets.
|
|
43
52
|
3. **Connect your AI.** Install and sign in to the
|
|
44
53
|
[Codex CLI](https://developers.openai.com/codex/cli) or
|
|
45
54
|
[Claude Code](https://code.claude.com/docs/en/quickstart), then choose your
|
|
@@ -73,7 +82,7 @@ value from an execution quote. Missing prices and holdings stay unknown;
|
|
|
73
82
|
transfer inactivity needs transfer-history evidence. Testnets and pool liquidity
|
|
74
83
|
are excluded from your portfolio's USD value.
|
|
75
84
|
|
|
76
|
-

|
|
77
86
|
|
|
78
87
|
*Markets, paired token logos and each wallet's balance in one view.*
|
|
79
88
|
|
|
@@ -81,7 +90,8 @@ are excluded from your portfolio's USD value.
|
|
|
81
90
|
|
|
82
91
|
- **Watch an existing wallet** using its public address. Watching does not grant
|
|
83
92
|
Kira permission to spend.
|
|
84
|
-
- **Create an
|
|
93
|
+
- **Create an OWS wallet** with **Create wallet** in the sidebar, powered by
|
|
94
|
+
[Open Wallet Standard](https://openwallet.sh/).
|
|
85
95
|
Enter the encryption passphrase in the local form. Kira connects the public
|
|
86
96
|
EVM account; keys stay in the local OWS vault.
|
|
87
97
|
- **Choose what AI can see.** Wallet access can be off, limited to one wallet,
|
|
@@ -108,6 +118,12 @@ package. Use `--data-dir /absolute/private/directory` before a command to choose
|
|
|
108
118
|
another workspace. For connection, model or provider help, see the
|
|
109
119
|
[CLI guide](docs/CLI.md) and [viewer guide](viewer/README.md).
|
|
110
120
|
|
|
121
|
+
From 0.1.4, a partial wallet analysis explains disabled discovery, missing
|
|
122
|
+
credentials and network failures directly beside the holdings. After connecting
|
|
123
|
+
discovery, use **Refresh holdings** to create a new analysis. **Refresh prices**
|
|
124
|
+
does not discover missing tokens. Existing workspace provider settings and
|
|
125
|
+
published snapshots are preserved.
|
|
126
|
+
|
|
111
127
|
## Build and contribute
|
|
112
128
|
|
|
113
129
|
```sh
|
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -20,4 +20,11 @@ The SDK is an optional dependency used for human-submitted encrypted wallet
|
|
|
20
20
|
creation and public account listing. See
|
|
21
21
|
[Open Wallet Standard](https://github.com/open-wallet-standard/core).
|
|
22
22
|
|
|
23
|
+
The official Open Wallet Standard wordmark in `viewer/static/ows-logo.svg` and
|
|
24
|
+
`site/assets/ows-logo.svg` is copied unchanged from
|
|
25
|
+
[openwallet.sh](https://openwallet.sh/footer-logo.svg). CSS displays its original
|
|
26
|
+
white artwork in black on light backgrounds. The mark belongs to its owner and
|
|
27
|
+
identifies the OWS wallet-creation integration without implying endorsement.
|
|
28
|
+
Its retrieval date and source digest are in `site/assets/ows-artwork-source.json`.
|
|
29
|
+
|
|
23
30
|
CASHCAT, BLAST and USDB artwork in the public reconstructed demo comes from lpTOKEN.fun token metadata, DEX Screener and CoinGecko. Token marks belong to their respective owners and identify the assets without implying affiliation or endorsement. The source URLs and chain-specific contract identities are recorded in `site/assets/token-artwork-sources.json`.
|
package/docs/CLI.md
CHANGED
|
@@ -3,32 +3,50 @@
|
|
|
3
3
|
The package carries the Python engine, browser assets and declared viem
|
|
4
4
|
dependency. It needs no Python packages and no sibling project. The package
|
|
5
5
|
file list excludes wallets, snapshots, conversations, caches, logs and private settings.
|
|
6
|
-
Install
|
|
6
|
+
Install Kira from npm, then run `kira setup`:
|
|
7
7
|
|
|
8
8
|
```sh
|
|
9
|
-
npm install -g
|
|
9
|
+
npm install -g kira-wallet
|
|
10
10
|
kira setup
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
The
|
|
13
|
+
The operator published npm version 0.1.3 on 2026-10-06. GitHub v0.1.3 remains
|
|
14
|
+
a draft; the previous archive and checksum are available from the
|
|
15
|
+
[0.1.2 GitHub release](https://github.com/realproject7/kira-wallet/releases/tag/v0.1.2).
|
|
14
16
|
Registry publication remains an operator action. The code and original character assets use the MIT license.
|
|
15
17
|
|
|
16
18
|
## Lifecycle
|
|
17
19
|
|
|
18
|
-
`kira
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
Use `kira setup` for your first setup. For everyday use, `kira start` starts
|
|
21
|
+
the local app with browser controls and opens it in your browser. The default
|
|
22
|
+
port is 8787. Use `--port` to choose another port, or `--no-open` to print the
|
|
23
|
+
URL without opening a browser. `kira status` identifies the managed instance.
|
|
24
|
+
`kira stop` checks its live instance ID and OS process identity before stopping it.
|
|
25
|
+
|
|
26
|
+
`kira init` prepares an empty private data directory. `kira start --read-only`
|
|
27
|
+
starts a loopback-only viewer without wallet or research actions.
|
|
21
28
|
Another portfolio never silently reuses that server.
|
|
22
29
|
|
|
23
30
|
`kira setup` starts the loopback viewer with local controls and opens its setup
|
|
24
31
|
screen. Use `--no-open` to print its URL instead. If a read-only viewer is already
|
|
25
|
-
running, stop it explicitly before setup
|
|
32
|
+
running, stop it explicitly before setup or a normal start. Changing between
|
|
33
|
+
read-only and control modes requires an explicit stop and restart. Starting
|
|
34
|
+
an already-running app in the same mode reuses its URL. The old `--controls`
|
|
35
|
+
option remains accepted for existing scripts but is no longer needed.
|
|
36
|
+
|
|
37
|
+
These startup defaults apply from published 0.1.3. In the older 0.1.2 package, use
|
|
38
|
+
`kira setup` or `kira start --controls` for the full app.
|
|
26
39
|
|
|
27
|
-
The 0.1.
|
|
40
|
+
The 0.1.2 GitHub release starts with a wallet checklist. Add a public EVM
|
|
28
41
|
address or use the browser wallet picker. Registration starts one explicit
|
|
29
42
|
research job; follow it in Activity and read the saved portfolio report.
|
|
30
43
|
No signature, seed phrase or private key is needed.
|
|
31
44
|
|
|
45
|
+
Candidate 0.1.4 explains limited discovery before wallet registration and beside
|
|
46
|
+
holdings. Missing coverage keeps the portfolio estimate partial. It is not yet
|
|
47
|
+
published to npm. Existing snapshots require a holdings refresh after connecting
|
|
48
|
+
an indexer; a price refresh does not discover missing tokens.
|
|
49
|
+
|
|
32
50
|
Public RPC checks known assets and Mint Club. For broader ERC20 discovery,
|
|
33
51
|
open Research connections, select Alchemy and name the private environment
|
|
34
52
|
variable holding its key. Never paste the key into the browser. `kira doctor`
|
|
@@ -38,8 +56,8 @@ prove complete coverage; inspect the recorded results and gaps.
|
|
|
38
56
|
Connecting Codex or Claude is optional. Use Connect your AI when ready, verify
|
|
39
57
|
a generic response and explicitly choose context permissions. Assistant answers
|
|
40
58
|
render a safe Markdown subset; raw HTML, images and links remain inert text.
|
|
41
|
-
|
|
42
|
-
0.1.0 package
|
|
59
|
+
The wallet-first setup is included in published npm 0.1.3. The historical
|
|
60
|
+
0.1.0 package uses the earlier account-first setup.
|
|
43
61
|
|
|
44
62
|
## Your model account
|
|
45
63
|
|
|
@@ -150,9 +168,9 @@ cancelled jobs retain their last good result and unpublished evidence.
|
|
|
150
168
|
interrupted jobs. Price jobs retain immutable overlays before updating the
|
|
151
169
|
viewer projection. A viewer stop does not cancel research jobs.
|
|
152
170
|
|
|
153
|
-
Start with `kira start
|
|
154
|
-
|
|
155
|
-
|
|
171
|
+
Start with `kira start` to use protected browser actions. Use `--read-only`
|
|
172
|
+
for a viewer without those actions. Stop an existing instance before switching
|
|
173
|
+
modes. Actions need the local session and same origin.
|
|
156
174
|
The supported URL uses 127.0.0.1. Browser controls offer wallet registration,
|
|
157
175
|
exact names, holdings/price refresh, cancel/resume, saved analysis comparison,
|
|
158
176
|
RPC references and discovery references. The synthetic demo cannot start
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Token discovery patch acceptance
|
|
2
|
+
|
|
3
|
+
Version: 0.1.4 candidate. Published npm 0.1.3 remains immutable.
|
|
4
|
+
|
|
5
|
+
## Problem and correction
|
|
6
|
+
|
|
7
|
+
A new personal data directory defaults to public RPC and no general ERC20
|
|
8
|
+
indexer. The installed app does not inherit discovery settings from a separate
|
|
9
|
+
research workspace. Public RPC reads native balances and enumerates Mint Club
|
|
10
|
+
assets, but cannot identify arbitrary ERC20 contracts for a wallet. Previously,
|
|
11
|
+
a finished analysis could show only native holdings without explaining this
|
|
12
|
+
configuration at the point of use.
|
|
13
|
+
|
|
14
|
+
The wallet registration dialog now explains limited discovery and offers a
|
|
15
|
+
connection action before research. The wallet overview explains disabled
|
|
16
|
+
discovery, missing credentials, stale analysis or incomplete provider checks.
|
|
17
|
+
It labels incomplete portfolio estimates as partial. Network details retain
|
|
18
|
+
separate discovery, RPC and registry coverage. Read-only and sample views do
|
|
19
|
+
not acquire research permissions.
|
|
20
|
+
|
|
21
|
+
RPC and candidate-balance failures now receive an explicit warning with
|
|
22
|
+
expanded affected-network details. Retry holdings starts the existing gated
|
|
23
|
+
research action; Review RPC settings opens the existing connection dialog.
|
|
24
|
+
The warning says to wait briefly before retrying and review the connection
|
|
25
|
+
or choose another provider if failures persist. Missing balances remain
|
|
26
|
+
unknown. Successful discovery alone cannot mark failed balance reads complete,
|
|
27
|
+
and old snapshots can project their fixed balance-failure note safely.
|
|
28
|
+
|
|
29
|
+
Connecting an indexer does not change an immutable saved analysis. Refresh
|
|
30
|
+
holdings runs discovery again. Refresh prices only updates existing holdings.
|
|
31
|
+
Discovery settings preserve existing explorer preferences, and the UI rereads
|
|
32
|
+
readiness after the settings job succeeds.
|
|
33
|
+
|
|
34
|
+
Registry diagnostics now retain up to five redacted error examples and numeric
|
|
35
|
+
HTTP or RPC codes in local evidence. The public model exposes only status and
|
|
36
|
+
counts. Oversized multicalls split at the same observed block, including errors
|
|
37
|
+
that viem converts into failed result rows. Transport outages and throttling
|
|
38
|
+
remain bounded instead of retrying every contract separately.
|
|
39
|
+
|
|
40
|
+
## Verification
|
|
41
|
+
|
|
42
|
+
- Required `npm test` suite passed: 152 Python tests, required Node behavior
|
|
43
|
+
checks and browser script syntax checks.
|
|
44
|
+
- `scripts/check-install.py` passed with a fresh 73-member package installation
|
|
45
|
+
and startup/shutdown lifecycle in a temporary synthetic workspace.
|
|
46
|
+
- The tracked-source privacy scan passed with the active private data directory
|
|
47
|
+
included. The full-history scan retains the previously recorded non-noreply
|
|
48
|
+
merge identity at `2ed52a2`; no new finding was introduced. That historical
|
|
49
|
+
identity is not reproduced here and public history was not rewritten.
|
|
50
|
+
- Actual viem with an in-memory aggregate3 transport: eight calls rejected
|
|
51
|
+
above two calls per batch succeed in seven requests, all at block 103.
|
|
52
|
+
Independent review also checked that HTTP 429 and 503 stay at one request
|
|
53
|
+
and isolated contract failures remain isolated. No live RPC is used by this
|
|
54
|
+
regression.
|
|
55
|
+
- Missing, unreadable and disabled discovery credentials produce explicit
|
|
56
|
+
coverage status without exposing credentials or attempting discovery.
|
|
57
|
+
- Viewer projection keeps raw provider diagnostics private and preserves
|
|
58
|
+
unknown prices. UI behavior checks cover disabled discovery, missing
|
|
59
|
+
credentials, stale analysis after connection, read-only actions and
|
|
60
|
+
partially successful explorer coverage.
|
|
61
|
+
- RPC recovery regressions cover unavailable chains, registry failures,
|
|
62
|
+
non-Mint candidate-balance failures, mixed missing connections, unknown
|
|
63
|
+
valuations, read-only controls and removal after a successful analysis.
|
|
64
|
+
- Chrome fixture checks at 1440 x 900 and 390 x 844: partial estimate and
|
|
65
|
+
discovery notice render without horizontal overflow. Both connection actions
|
|
66
|
+
open Research connections. The add-wallet dialog says Add with limited
|
|
67
|
+
discovery when the provider is absent. Fixture actions submitted no research,
|
|
68
|
+
account authorization or provider requests.
|
|
69
|
+
- RPC-failure fixtures at the same desktop/mobile sizes show expanded Base
|
|
70
|
+
and BNB Chain details, both recovery actions and a partial estimate without
|
|
71
|
+
horizontal overflow. Review RPC settings opens the connection dialog.
|
|
72
|
+
|
|
73
|
+
The design references were Mixpanel's dashboard connection guidance and
|
|
74
|
+
Databricks' inline data-check failure state, retrieved
|
|
75
|
+
with [Lazyweb](https://www.lazyweb.com/agentic-search/51280761-491a-4a3f-a78e-ed2dcd427ee8).
|
|
76
|
+
No operator wallet information or screenshots were submitted to that service.
|
|
77
|
+
|
|
78
|
+
Independent review found a viem failed-result handling defect during
|
|
79
|
+
implementation. The correction and actual-client regression were reviewed
|
|
80
|
+
again and the finding was closed. Screenshots and operator test evidence
|
|
81
|
+
remain in the ignored local ticket ledger.
|
|
82
|
+
|
|
83
|
+
## Acceptance limits and release boundary
|
|
84
|
+
|
|
85
|
+
Operator-approved reuse of an existing discovery connection recovered
|
|
86
|
+
non-native holdings in a new saved analysis. Some provider and network gaps
|
|
87
|
+
remain; this does not establish complete holdings or verify every expected
|
|
88
|
+
token. Those limits remain visible in coverage. The existing operator server
|
|
89
|
+
was not restarted or globally upgraded during patch verification.
|
|
90
|
+
|
|
91
|
+
The source/package privacy scan is required before preparation.
|
|
92
|
+
`scripts/prepare-release.py`
|
|
93
|
+
pins the clean source commit and archive checksum. Registry publication
|
|
94
|
+
remains operator-only. No npm publication, GitHub release publication or PR
|
|
95
|
+
merge is part of preparing this candidate.
|
package/docs/RELEASE.md
CHANGED
|
@@ -3,9 +3,15 @@
|
|
|
3
3
|
## GitHub and npm distribution
|
|
4
4
|
|
|
5
5
|
GitHub version releases and npm registry publication are separate. The GitHub
|
|
6
|
-
0.1.
|
|
6
|
+
0.1.2 release provides the reviewed `kira-wallet-0.1.2.tgz`, its SHA-256 file and
|
|
7
7
|
release manifest. Its tag pins the source used to create those assets. Install
|
|
8
|
-
that archive
|
|
8
|
+
that archive from GitHub. The operator published npm 0.1.3 on 2026-10-06;
|
|
9
|
+
its archive and installed bytes were verified separately. GitHub v0.1.3
|
|
10
|
+
remains a draft. GitHub and registry status must not be inferred from each other.
|
|
11
|
+
Version 0.1.2 was published to GitHub on 2026-10-05 from
|
|
12
|
+
`f994e99acb4e2d778fb4c95479f04355c88c00b3`. Its archive SHA-256 is
|
|
13
|
+
`866b7f1ad543bff1ff59d3bf840eca6313eb6e206e78b8611186e5a971153f28`.
|
|
14
|
+
The released archive and manifest stay immutable when installation guidance changes.
|
|
9
15
|
The historical 0.1.0 GitHub release points to `cfa91659a92b14467c7458d27508157f5fd615a9`
|
|
10
16
|
and preserves the exact already-published npm archive.
|
|
11
17
|
|
|
@@ -17,8 +23,42 @@ their invented wallet addresses, balances and seeded chat are documented in
|
|
|
17
23
|
|
|
18
24
|
## npm publication preparation
|
|
19
25
|
|
|
20
|
-
|
|
21
|
-
|
|
26
|
+
Published **0.1.3** makes `kira start` open the full local
|
|
27
|
+
app by default. `--read-only` preserves the viewer-only mode, `--no-open`
|
|
28
|
+
supports terminal-only use, and `--controls` remains compatible with existing
|
|
29
|
+
scripts. A running instance never changes modes silently. Local session,
|
|
30
|
+
origin, AI-context and wallet-operation checks remain in place. New-wallet
|
|
31
|
+
creation now identifies Open Wallet Standard with its official locally served
|
|
32
|
+
logo and explains encrypted local storage and public-account research. The
|
|
33
|
+
OWS integration and wallet-operation permissions are unchanged.
|
|
34
|
+
|
|
35
|
+
The next candidate is **0.1.4**. It makes incomplete token discovery visible
|
|
36
|
+
before registration and beside wallet holdings, separates disabled discovery
|
|
37
|
+
from missing credentials and provider failures, and offers a connection or
|
|
38
|
+
holdings-refresh action. Registry failures retain bounded redacted examples
|
|
39
|
+
and RPC error codes. Oversized multicalls split into smaller batches while
|
|
40
|
+
transport outages remain bounded. Existing explorer preferences are preserved
|
|
41
|
+
when discovery settings are saved. Connecting an indexer and refreshing
|
|
42
|
+
holdings are still required to recover tokens absent from an older snapshot.
|
|
43
|
+
RPC failures show affected networks, retry guidance and a settings action.
|
|
44
|
+
Failed candidate balance reads keep the saved result and valuation partial
|
|
45
|
+
even when general token discovery succeeded.
|
|
46
|
+
|
|
47
|
+
Version 0.1.4 requires independent review and operator registry publication.
|
|
48
|
+
It is not yet available from the registry. The showcase HTML animation is
|
|
49
|
+
deployed separately and requires no npm release. Keep published 0.1.2 and
|
|
50
|
+
0.1.3 package bytes immutable. See [discovery acceptance](KIRA_DISCOVERY_ACCEPTANCE.md).
|
|
51
|
+
|
|
52
|
+
The operator published npm **0.1.2** on 2026-10-05. Its registry archive was
|
|
53
|
+
downloaded and matched the reviewed GitHub archive above, including SHA-256,
|
|
54
|
+
the registry SHA-1 and SHA-512 integrity value. A fresh registry installation
|
|
55
|
+
matched all 71 archive members and passed local viewer startup and shutdown.
|
|
56
|
+
The installed OWS SDK is available. The three existing local viewers were
|
|
57
|
+
upgraded with their wallet records, model permissions and current messages
|
|
58
|
+
preserved. A generic native AI connection check succeeded without portfolio
|
|
59
|
+
context. Runtime evidence remains private.
|
|
60
|
+
|
|
61
|
+
Version **0.1.2** includes
|
|
22
62
|
the prior wallet setup, scoped native AI, token artwork, compact wallet/pool
|
|
23
63
|
layouts and README improvements, plus fixes found through real local testing.
|
|
24
64
|
See [local live acceptance](KIRA_LIVE_QA.md) for the test scope and findings.
|
|
@@ -29,8 +69,9 @@ The earlier unpublished 0.1.2 candidate is preserved in the ignored
|
|
|
29
69
|
`dist/preserved-0.1.2-bad8f02/` directory. Use the current `dist/release.json`
|
|
30
70
|
and its matching archive for publication; do not mix manifests from candidates.
|
|
31
71
|
|
|
32
|
-
Keep GitHub v0.1.1, its tag and its released archive immutable.
|
|
33
|
-
|
|
72
|
+
Keep GitHub v0.1.1, its tag and its released archive immutable. Versions 0.1.2
|
|
73
|
+
and 0.1.3 are also published and immutable. Do not rebuild them from later documentation
|
|
74
|
+
commits. Future package changes need a new version. Release preparation does not
|
|
34
75
|
publish a GitHub release, merge PR 19, promote the website or publish to npm.
|
|
35
76
|
Registry publication remains operator-only. The coding agent never publishes.
|
|
36
77
|
|
|
@@ -44,12 +85,12 @@ python3 scripts/prepare-release.py
|
|
|
44
85
|
```
|
|
45
86
|
|
|
46
87
|
`prepare-release.py` scans the package boundary and creates
|
|
47
|
-
`dist/kira-wallet-0.1.
|
|
88
|
+
`dist/kira-wallet-0.1.4.tgz`. The ignored `dist/release.json` records its source
|
|
48
89
|
commit, SHA-256 and member count. It never publishes. After reviewing that
|
|
49
90
|
manifest, the operator's final command from the project root is:
|
|
50
91
|
|
|
51
92
|
```sh
|
|
52
|
-
npm publish ./dist/kira-wallet-0.1.
|
|
93
|
+
npm publish ./dist/kira-wallet-0.1.4.tgz --access public
|
|
53
94
|
```
|
|
54
95
|
|
|
55
96
|
Use the operator's npm account and complete authentication or OTP personally.
|
|
@@ -63,16 +104,17 @@ The operator published 0.1.0, verified on 2026-10-04. The registry archive has
|
|
|
63
104
|
`45c33d4e37bd45145c53afd05a234caa746146a47e2f729fec1087d841506858`,
|
|
64
105
|
matching the prepared release at `cfa9165`. Installation is
|
|
65
106
|
`npm install -g kira-wallet`, then `kira setup`. The public installation page
|
|
66
|
-
uses
|
|
67
|
-
reach npm users.
|
|
107
|
+
now uses the published npm 0.1.2 package. Changes after a published source commit
|
|
108
|
+
require a new registry version to reach npm users.
|
|
68
109
|
|
|
69
110
|
## Public website
|
|
70
111
|
|
|
71
112
|
Only `site/` is deployed to Vercel's `kira-wallet` project. It contains the
|
|
72
|
-
|
|
113
|
+
landing page, native HTML conversation animation, character assets
|
|
73
114
|
and installation instructions. The companion proposal is retired; its URLs redirect
|
|
74
115
|
to the landing page. No wallet input, portfolio API, chat API or analytics service
|
|
75
|
-
is hosted. The site permits only its own small
|
|
116
|
+
is hosted. The site permits only its own small animation controller and local
|
|
117
|
+
artwork. External network requests and forms stay blocked. The local viewer
|
|
76
118
|
remains loopback-only.
|
|
77
119
|
|
|
78
120
|
`kirawallet.app` uses Vercel nameservers and managed HTTPS. Keep `.vercel/`
|
package/kira_cli.py
CHANGED
|
@@ -45,8 +45,12 @@ def live_record(root):
|
|
|
45
45
|
|
|
46
46
|
def start(root,port,controls=False):
|
|
47
47
|
if item:=live_record(root):
|
|
48
|
-
if
|
|
49
|
-
|
|
48
|
+
if bool((health(item['port']) or {}).get('controls')) != controls:
|
|
49
|
+
mode='local controls' if controls else 'read-only mode'
|
|
50
|
+
command='kira start' if controls else 'kira start --read-only'
|
|
51
|
+
raise ValueError(f'The running app uses a different mode. Run kira stop, then {command} to use {mode}.')
|
|
52
|
+
url=f"http://127.0.0.1:{item['port']}"
|
|
53
|
+
print(json.dumps({'status':'running','url':url}));return url
|
|
50
54
|
if health(port):raise ValueError('Port belongs to another Kira data directory. Choose --port.')
|
|
51
55
|
instance=str(uuid.uuid4());env={**os.environ,'KIRA_INSTANCE_ID':instance}
|
|
52
56
|
with (root/'viewer.log').open('ab') as log:
|
|
@@ -56,7 +60,8 @@ def start(root,port,controls=False):
|
|
|
56
60
|
if child.poll() is not None:raise ValueError('Viewer could not start. Check the private viewer.log or choose another port.')
|
|
57
61
|
if (health(port) or {}).get('instance')==instance:
|
|
58
62
|
atomic(root/'viewer-process.json',{'pid':child.pid,'port':port,'instance':instance})
|
|
59
|
-
|
|
63
|
+
url=f'http://127.0.0.1:{port}'
|
|
64
|
+
print(json.dumps({'status':'running','url':url}));return url
|
|
60
65
|
time.sleep(.1)
|
|
61
66
|
child.terminate();raise ValueError('Viewer startup timed out.')
|
|
62
67
|
|
|
@@ -143,7 +148,12 @@ def main():
|
|
|
143
148
|
parser=argparse.ArgumentParser(description=__doc__)
|
|
144
149
|
parser.add_argument('--data-dir',default=os.environ.get('KIRA_DATA_DIR',str(Path.home()/'.local/share/kira-wallet')))
|
|
145
150
|
sub=parser.add_subparsers(dest='command',required=True)
|
|
146
|
-
sub.add_parser('init');s=sub.add_parser('start');s.add_argument('--port',type=int,default=8787)
|
|
151
|
+
sub.add_parser('init');s=sub.add_parser('start');s.add_argument('--port',type=int,default=8787)
|
|
152
|
+
mode=s.add_mutually_exclusive_group()
|
|
153
|
+
mode.add_argument('--read-only',dest='controls',action='store_false',help='View recorded holdings without local browser actions.')
|
|
154
|
+
mode.add_argument('--controls',dest='controls',action='store_true',help=argparse.SUPPRESS)
|
|
155
|
+
s.set_defaults(controls=True)
|
|
156
|
+
s.add_argument('--no-open',action='store_true',help='Print the local app URL without opening a browser.')
|
|
147
157
|
setup=sub.add_parser('setup');setup.add_argument('--port',type=int,default=8787);setup.add_argument('--no-open',action='store_true')
|
|
148
158
|
sub.add_parser('stop');sub.add_parser('status');sub.add_parser('wallets')
|
|
149
159
|
sub.add_parser('tools')
|
|
@@ -172,7 +182,7 @@ def main():
|
|
|
172
182
|
serve(root);return 0
|
|
173
183
|
if getattr(args,'resume',None) and (args.enqueue or args.idempotency_key):raise ValueError('Legacy snapshot resume cannot enqueue or use an idempotency key. Use jobs resume for durable jobs.')
|
|
174
184
|
root.mkdir(parents=True,exist_ok=True,mode=0o700)
|
|
175
|
-
operation_lock=None
|
|
185
|
+
operation_lock=None;viewer_lock=None
|
|
176
186
|
if args.command in ('init','demo','rpc','discovery') or args.command=='config' and args.setting!='show':
|
|
177
187
|
import fcntl
|
|
178
188
|
operation_lock=(root/'.analysis.lock').open('a')
|
|
@@ -183,15 +193,17 @@ def main():
|
|
|
183
193
|
viewer_lock=(root/'.viewer.lock').open('a')
|
|
184
194
|
try:fcntl.flock(viewer_lock,fcntl.LOCK_EX|fcntl.LOCK_NB)
|
|
185
195
|
except BlockingIOError:raise ValueError('Another viewer lifecycle operation is active.') from None
|
|
186
|
-
initialize(root)
|
|
187
196
|
from kira_config import load_config,config_path,redact
|
|
188
197
|
try:
|
|
198
|
+
initialize(root)
|
|
189
199
|
if args.command=='init':print(json.dumps({'status':'initialized','rpc_mode':load_config()['rpc']['mode']}))
|
|
190
|
-
elif args.command=='start':
|
|
200
|
+
elif args.command=='start':
|
|
201
|
+
url=start(root,args.port,args.controls)
|
|
202
|
+
if not args.no_open:
|
|
203
|
+
import webbrowser
|
|
204
|
+
webbrowser.open(url)
|
|
191
205
|
elif args.command=='setup':
|
|
192
|
-
start(root,args.port,True)
|
|
193
|
-
item=live_record(root)
|
|
194
|
-
url=f"http://127.0.0.1:{item['port']}/?setup=1"
|
|
206
|
+
url=start(root,args.port,True)+'/?setup=1'
|
|
195
207
|
if not args.no_open:
|
|
196
208
|
import webbrowser
|
|
197
209
|
webbrowser.open(url)
|
|
@@ -257,6 +269,9 @@ def main():
|
|
|
257
269
|
return subprocess.run(command).returncode
|
|
258
270
|
except (ValueError,OSError) as error:
|
|
259
271
|
print(redact(error),file=sys.stderr);return 1
|
|
272
|
+
finally:
|
|
273
|
+
if viewer_lock:viewer_lock.close()
|
|
274
|
+
if operation_lock:operation_lock.close()
|
|
260
275
|
return 0
|
|
261
276
|
|
|
262
277
|
if __name__=='__main__':
|
package/kira_jobs.py
CHANGED
|
@@ -403,7 +403,7 @@ class JobStore:
|
|
|
403
403
|
previous=config['rpc']['chains'].get(key,{})
|
|
404
404
|
if row['url_env']==previous.get('url_env') and previous.get('alchemy_network'):row['alchemy_network']=previous['alchemy_network']
|
|
405
405
|
config['rpc']=updated
|
|
406
|
-
else:config['discovery']={**job['input'],'explorers':False}
|
|
406
|
+
else:config['discovery']={**job['input'],'explorers':config['discovery'].get('explorers',False)}
|
|
407
407
|
atomic(config_path(),config)
|
|
408
408
|
result={'status':'completed','setting':job['operation']};atomic(self.jobs/'results'/(job['job_id']+'.json'),result);return result
|
|
409
409
|
env = {**os.environ, 'KIRA_DATA_DIR': str(self.root), 'KIRA_JOB_ID': job['job_id'], 'KIRA_JOB_SNAPSHOT': job['checkpoint'], 'KIRA_ANALYSIS_FD': str(writer.fileno()), 'PYTHONUNBUFFERED': '1'}
|
package/onchain.cjs
CHANGED
|
@@ -52,7 +52,12 @@ function safeError(error){
|
|
|
52
52
|
message=message.replace(/https?:\/\/[^\s"<>]+/g,'[RPC URL]');
|
|
53
53
|
message=message.replace(/(bearer\s+)[^\s,;"<>]+/gi,'$1[redacted]');
|
|
54
54
|
message=message.replace(/(authorization|api[-_]?key|bearer)([\s:=]+)[^\s,;"<>]+/gi,'$1$2[redacted]');
|
|
55
|
-
|
|
55
|
+
const diagnostic={error_type:error.name,message:message.slice(0,500)};
|
|
56
|
+
for(let cause=error,depth=0;cause&&depth<6;cause=cause.cause,depth++){
|
|
57
|
+
if(Number.isInteger(cause.status))diagnostic.http_status=cause.status;
|
|
58
|
+
if(Number.isInteger(cause.code))diagnostic.rpc_code=cause.code;
|
|
59
|
+
}
|
|
60
|
+
return diagnostic;
|
|
56
61
|
}
|
|
57
62
|
function rpcEndpointsUsed(c){return [...(endpointReads.get(c)||[])].filter(i=>i>=0).sort((a,b)=>a-b);}
|
|
58
63
|
module.exports={viem,root,assets,networks,bondAbi,endpoints,client,connect,resilientTransport,rpcEndpointsUsed,serialize,safeError};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kira-wallet",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Manage your wallets with your own AI. Track tokens, research markets and refresh holdings in a local workspace.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"private": false,
|
|
@@ -55,7 +55,8 @@
|
|
|
55
55
|
"kira_research_tools.py",
|
|
56
56
|
"kira_ows.py",
|
|
57
57
|
"ows_bridge.cjs",
|
|
58
|
-
"docs/KIRA_LIVE_QA.md"
|
|
58
|
+
"docs/KIRA_LIVE_QA.md",
|
|
59
|
+
"docs/KIRA_DISCOVERY_ACCEPTANCE.md"
|
|
59
60
|
],
|
|
60
61
|
"scripts": {
|
|
61
62
|
"test": "python3 -m unittest discover -s . -p 'test_*.py' && python3 -m unittest discover -s viewer -p 'test_*.py' && node test-onchain.cjs && node --check viewer/static/app.js && node --check viewer/static/jobs.js && node test-workspace.cjs && node --check viewer/static/workspace.js && node --check viewer/static/workspace-model.js && node test-watching.cjs && node test-watching-ui.cjs && node --check viewer/static/watching.js && node --check viewer/static/watching-model.js && node --check viewer/static/agent.js && node test-agent-ui.cjs && node test-markdown.cjs && node --check viewer/static/report.js && node test-report-ui.cjs && node test-holdings-ui.cjs && node test-rpc-fallback.cjs && node test-ows.cjs && node test-ows-ui.cjs && node --check viewer/static/ows.js && node --check viewer/static/chat-workspace.js && node --check viewer/static/selects.js && node --check site/demo.js"
|
package/pipeline-onchain.cjs
CHANGED
|
@@ -48,12 +48,28 @@ async function connect(n) {
|
|
|
48
48
|
}
|
|
49
49
|
throw error || new Error('No RPC endpoint configured; public fallback is disabled.');
|
|
50
50
|
}
|
|
51
|
+
function rpcFailure(error) {
|
|
52
|
+
let sizeLimit=false,transportFailure=false;
|
|
53
|
+
for(let cause=error,depth=0;cause&&depth<10;cause=cause.cause,depth++) {
|
|
54
|
+
if(['HttpRequestError','TimeoutError'].includes(cause.name))transportFailure=true;
|
|
55
|
+
if(cause.status===413||/\b(batch|payload|response|request body)\b.*\b(large|size|limit|exceed)/i.test(cause.shortMessage||cause.message||'')||/gas.*(cap|limit).*exceed|exceed.*gas.*(cap|limit)/i.test(cause.shortMessage||cause.message||''))sizeLimit=true;
|
|
56
|
+
if(cause.status===429)return {sizeLimit:false,transportFailure:true};
|
|
57
|
+
}
|
|
58
|
+
return {sizeLimit,transportFailure};
|
|
59
|
+
}
|
|
51
60
|
async function calls(c, contracts, block) {
|
|
52
61
|
if (!contracts.length) return [];
|
|
53
62
|
try {
|
|
54
|
-
|
|
63
|
+
const results=await c.multicall({contracts,blockNumber:block,batchSize:0,multicallAddress:MULTICALL});
|
|
64
|
+
// viem allowFailure also wraps an aggregate RPC failure as per-contract rows.
|
|
65
|
+
if(results.length&&results.every(r=>r.status==='failure')) {
|
|
66
|
+
const limited=results.find(r=>rpcFailure(r.error).sizeLimit);
|
|
67
|
+
if(limited)throw limited.error;
|
|
68
|
+
}
|
|
69
|
+
return results;
|
|
55
70
|
} catch (e) {
|
|
56
|
-
|
|
71
|
+
const {sizeLimit,transportFailure}=rpcFailure(e);
|
|
72
|
+
if (!sizeLimit&&transportFailure) return contracts.map(()=>({status:'failure',error:e}));
|
|
57
73
|
if (contracts.length > 1) {
|
|
58
74
|
const mid = Math.ceil(contracts.length/2);
|
|
59
75
|
return [...await calls(c,contracts.slice(0,mid),block), ...await calls(c,contracts.slice(mid),block)];
|
|
@@ -135,6 +151,7 @@ async function scanChain(input, n, output) {
|
|
|
135
151
|
cache_reused:reg.cache_reused,balance_errors:errors.filter(t=>t.registered_mintclub).length,
|
|
136
152
|
positive_balances:held.filter(t=>t.registered_mintclub).length,block_number:block,complete:reg.errors===0 && checked===reg.count,
|
|
137
153
|
source:'On-chain Mint Club bond tokenCount()/tokens(index), then direct wallet balance calls',bond_address:n.mintclub_bond_address};
|
|
154
|
+
row.registry_scan.error_examples=reg.entries.filter(t=>t.error||!t.address).slice(0,5).map(t=>({index:t.index,error:t.error||{message:'Registry address unavailable'}}));
|
|
138
155
|
row.balance_errors = errors;
|
|
139
156
|
for (const t of held) {
|
|
140
157
|
const item = {chain_id:n.chain_id,network:n.network,token_address:t.address,token_type:t.token_type,token_id:t.token_type==='ERC1155'?'0':null,
|
package/viewer/model.py
CHANGED
|
@@ -196,6 +196,11 @@ def project_wallet(entry, snapshot, root=ROOT, images=None):
|
|
|
196
196
|
'links':[{'label':'Explorer','url':EXPLORERS[c['chain_id']]+'/address/'+entry['address']}] if c['chain_id'] in EXPLORERS else []})
|
|
197
197
|
chains=[]
|
|
198
198
|
for c in coverage:
|
|
199
|
+
# Older snapshots retained this count only in a fixed pipeline note.
|
|
200
|
+
failed_balances=c.get('candidate_balance_errors')
|
|
201
|
+
if type(failed_balances) is not int or failed_balances<0:
|
|
202
|
+
failed_balances=max((int(m.group(1)) for note in c.get('notes',[]) or [] if isinstance(note,str)
|
|
203
|
+
and (m:=re.fullmatch(r'([0-9]{1,9}) candidate token balances could not be read\.',note))),default=0)
|
|
199
204
|
rows=[a for a in assets if a['chain_id']==c['chain_id']]
|
|
200
205
|
for a in rows:
|
|
201
206
|
a['environment']=c['environment']
|
|
@@ -204,6 +209,11 @@ def project_wallet(entry, snapshot, root=ROOT, images=None):
|
|
|
204
209
|
'image_url':chain_image(c['chain_id']),
|
|
205
210
|
'assets':len(rows),'value_usd':sum(a['value_usd'] for a in rows if a['value_usd'] is not None) if c['environment']=='mainnet' and any(a['value_usd'] is not None for a in rows) else None,
|
|
206
211
|
'complete':c['general_erc20_discovery']=='indexer_checked','rpc_available':c['rpc_status']=='available',
|
|
212
|
+
'discovery_status':c.get('discovery_status') if c.get('discovery_status') in ('checked','disabled','missing_credential','unsupported','provider_error') else 'incomplete',
|
|
213
|
+
'registry_complete':(c.get('mintclub_registry_scan') or {}).get('complete') is True,
|
|
214
|
+
'registry_errors':(c.get('mintclub_registry_scan') or {}).get('registry_errors',0),
|
|
215
|
+
'balance_errors':(c.get('mintclub_registry_scan') or {}).get('balance_errors',0),
|
|
216
|
+
'candidate_balance_errors':failed_balances,
|
|
207
217
|
'native_symbol':c.get('native_symbol'),
|
|
208
218
|
'native_balance':c.get('native_balance') if c['rpc_status']=='available' and number(c.get('native_balance')) is not None else None,
|
|
209
219
|
'native_observed_at':c.get('native_observed_at'),
|
package/viewer/server.py
CHANGED
|
@@ -141,7 +141,7 @@ class Handler(BaseHTTPRequestHandler):
|
|
|
141
141
|
if not entry or not entry.get('latest_snapshot',{}).get('report'):self.send_error(404);return
|
|
142
142
|
body=within(ROOT,entry['latest_snapshot']['report']).read_bytes()
|
|
143
143
|
self.respond(body,'text/plain; charset=utf-8');return
|
|
144
|
-
files={'/':'index.html','/index.html':'index.html','/app.js':'app.js','/jobs.js':'jobs.js','/workspace.js':'workspace.js','/workspace-model.js':'workspace-model.js','/workspace.css':'workspace.css','/agent.js':'agent.js','/ows.js':'ows.js','/agent.css':'agent.css','/chat-workspace.css':'chat-workspace.css','/chat-workspace.js':'chat-workspace.js','/selects.js':'selects.js','/markdown.js':'markdown.js','/report.js':'report.js','/report.css':'report.css','/watching.js':'watching.js','/watching-model.js':'watching-model.js','/style.css':'style.css','/favicon.svg':'favicon.svg','/kira.png':'kira.png','/kira-logo.png':'kira-logo.png',**{f'/kira-{pose}.png':f'kira-{pose}.png' for pose in ('research','explain','review','attention')}}
|
|
144
|
+
files={'/':'index.html','/index.html':'index.html','/app.js':'app.js','/jobs.js':'jobs.js','/workspace.js':'workspace.js','/workspace-model.js':'workspace-model.js','/workspace.css':'workspace.css','/agent.js':'agent.js','/ows.js':'ows.js','/agent.css':'agent.css','/chat-workspace.css':'chat-workspace.css','/chat-workspace.js':'chat-workspace.js','/selects.js':'selects.js','/markdown.js':'markdown.js','/report.js':'report.js','/report.css':'report.css','/watching.js':'watching.js','/watching-model.js':'watching-model.js','/style.css':'style.css','/favicon.svg':'favicon.svg','/kira.png':'kira.png','/kira-logo.png':'kira-logo.png','/ows-logo.svg':'ows-logo.svg',**{f'/kira-{pose}.png':f'kira-{pose}.png' for pose in ('research','explain','review','attention')}}
|
|
145
145
|
if path not in files:self.send_error(404);return
|
|
146
146
|
file=STATIC/files[path]
|
|
147
147
|
content={'html':'text/html; charset=utf-8','js':'text/javascript; charset=utf-8','css':'text/css; charset=utf-8','svg':'image/svg+xml','png':'image/png'}[file.suffix[1:]]
|
package/viewer/static/app.js
CHANGED
|
@@ -129,19 +129,43 @@ function renderWallet(){
|
|
|
129
129
|
$('wallet-tags').innerHTML=w.tags.slice(1).map(t=>`<span class="tag">${escapeHTML(t)}</span>`).join(' ');
|
|
130
130
|
$('report-link').hidden=!w.report_url;if(w.report_url)$('report-link').href=safeURL(w.report_url);
|
|
131
131
|
$('total-value').textContent=w.analysed_at?money(w.known_value_usd):'—';
|
|
132
|
-
|
|
132
|
+
const partial=w.chains.some(c=>c.environment==='mainnet'&&KiraView.coverageGap(c));
|
|
133
|
+
$('valuation-note').textContent=w.analysed_at?(partial?'Partial portfolio · ':'')+(w.known_value_usd==null?(w.assets.length?'Value is unknown because prices are unavailable.':'No positive holdings recorded · unchecked networks remain unknown.'):'Market and curve spot estimates · excludes unpriced assets'):'Analysis will appear here when it is ready.';
|
|
133
134
|
$('asset-count').textContent=w.assets.length;$('unpriced-count').textContent=w.unpriced_count+' unpriced';
|
|
134
135
|
const mainnets=w.chains.filter(c=>c.environment==='mainnet');
|
|
135
136
|
$('chain-count').textContent=mainnets.filter(c=>c.assets>0).length;
|
|
136
137
|
const coverage=mainnets.filter(c=>c.complete).length;
|
|
137
|
-
$('coverage-note').textContent=coverage+' of '+mainnets.length+' checked
|
|
138
|
-
$('coverage-note').title=
|
|
138
|
+
$('coverage-note').textContent='Token discovery: '+coverage+' of '+mainnets.length+' mainnets checked';
|
|
139
|
+
$('coverage-note').title='Token discovery and on-chain balance checks are separate. See network coverage details for failures.';
|
|
139
140
|
$('sync-label').textContent=w.analysed_at?'Analysis '+stamp(w.analysed_at):'Awaiting analysis';
|
|
140
141
|
$('price-time').textContent=w.prices_at?'Prices '+stamp(w.prices_at):'';
|
|
141
|
-
const chainOptions=w.chains.filter(c=>c.assets>0
|
|
142
|
+
const chainOptions=w.chains.filter(c=>c.assets>0||KiraView.coverageGap(c));
|
|
142
143
|
if(selectedChain!=='all'&&!chainOptions.some(c=>String(c.id)===selectedChain))selectedChain='all';
|
|
143
|
-
$('chain-filter').innerHTML='<option value="all">All chains</option>'+chainOptions.map(c=>`<option value="${c.id}">${escapeHTML(c.name)}${c.environment==='testnet'?' · Testnet':''}${
|
|
144
|
-
$('chain-filter').value=selectedChain;renderHoldings();
|
|
144
|
+
$('chain-filter').innerHTML='<option value="all">All chains</option>'+chainOptions.map(c=>`<option value="${c.id}">${escapeHTML(c.name)}${c.environment==='testnet'?' · Testnet':''}${KiraView.coverageGap(c)?' · Incomplete':''}</option>`).join('');
|
|
145
|
+
$('chain-filter').value=selectedChain;renderHoldings();renderDiscoveryCoverage(w);
|
|
146
|
+
}
|
|
147
|
+
function renderDiscoveryCoverage(w) {
|
|
148
|
+
const readiness=typeof setupReadiness!=='undefined'?setupReadiness:null;
|
|
149
|
+
const notice=KiraView.discoveryNotice(w,readiness);
|
|
150
|
+
$('discovery-notice').hidden=!notice;
|
|
151
|
+
if(!notice)return;
|
|
152
|
+
$('discovery-notice-title').textContent=notice.title;
|
|
153
|
+
$('discovery-notice-text').textContent=notice.message;
|
|
154
|
+
const controls=typeof localSession!=='undefined'&&localSession?.controls===true&&state?.demo!==true;
|
|
155
|
+
$('discovery-settings').hidden=!controls||!(notice.action==='settings'||notice.settings);
|
|
156
|
+
$('discovery-settings').textContent=notice.settingsLabel||'Connect token discovery';
|
|
157
|
+
$('discovery-refresh').hidden=!controls||notice.action!=='refresh';
|
|
158
|
+
$('discovery-refresh').textContent=notice.refreshLabel||'Refresh holdings';
|
|
159
|
+
$('discovery-refresh').disabled=$('refresh-wallet').disabled;
|
|
160
|
+
$('discovery-notice').querySelector('details').open=notice.rpcIssues===true;
|
|
161
|
+
$('discovery-network-details').textContent=w.chains.filter(c=>c.environment==='mainnet'&&KiraView.coverageGap(c)).map(c=>{
|
|
162
|
+
const reasons=[];
|
|
163
|
+
if(!c.rpc_available)reasons.push('balance reads failed; retry holdings, then review RPC settings if this continues');
|
|
164
|
+
if(!c.complete)reasons.push(({disabled:'token discovery off',missing_credential:'indexer credential unavailable',unsupported:'indexer unsupported',provider_error:'indexer request failed'})[c.discovery_status]||'token discovery incomplete');
|
|
165
|
+
if(c.registry_complete===false&&c.rpc_available)reasons.push('Mint Club balances partly unchecked; retry holdings'+(c.registry_errors||c.balance_errors?' ('+(c.registry_errors||0)+' asset checks and '+(c.balance_errors||0)+' balance reads failed)':''));
|
|
166
|
+
if(c.candidate_balance_errors>0)reasons.push(c.candidate_balance_errors+' token balance reads failed; missing balances remain unknown');
|
|
167
|
+
return c.name+': '+reasons.join('; ');
|
|
168
|
+
}).join('\n');
|
|
145
169
|
}
|
|
146
170
|
function icon(asset){
|
|
147
171
|
if(typeof asset.symbol!=='string'||!asset.symbol)return ['', '?'];
|
package/viewer/static/index.html
CHANGED
|
@@ -50,6 +50,7 @@
|
|
|
50
50
|
<div id="wallet-actions" class="wallet-actions" hidden><button id="refresh-wallet" class="quiet-button" type="button"><svg class="ui-icon" viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M3 11a9 9 0 1 1 2.6 7M3 5v6h6"/></svg>Refresh holdings</button><button id="refresh-prices" class="quiet-button" type="button"><svg class="ui-icon" viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M4 17l5-5 4 3 7-10M15 5h5v5"/></svg>Refresh prices</button><button id="edit-tags" class="quiet-button" type="button"><svg class="ui-icon" viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M4 20l4-1L20 7l-3-3L5 16zM14 7l3 3"/></svg>Edit names</button><button id="view-history" class="quiet-button" type="button"><svg class="ui-icon" viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M5 4h14v16H5zM8 8h8M8 12h8M8 16h5"/></svg>Saved analyses</button></div>
|
|
51
51
|
<section class="wallet-heading"><div><div class="eyebrow">WALLET OVERVIEW</div><h1 id="wallet-name">Your wallets</h1><div class="wallet-address"><span id="address"></span><button id="copy-address" class="icon-button" aria-label="Copy wallet address" title="Copy wallet address"><svg viewBox="0 0 24 24" fill="none" aria-hidden="true"><rect x="8" y="8" width="11" height="12" rx="2"/><path d="M15 8V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h3"/></svg></button><span id="wallet-tags"></span></div></div><a id="report-link" class="text-link" target="_blank" rel="noreferrer">Research notes <span aria-hidden="true">↗</span></a></section>
|
|
52
52
|
<section class="portfolio-summary" aria-label="Portfolio summary"><div class="valuation"><div class="summary-label">Estimated portfolio value</div><div id="total-value" class="total-value">—</div><div id="valuation-note" class="valuation-note">Loading the latest analysis</div></div><div class="summary-stat"><span class="summary-label">Assets</span><strong id="asset-count">—</strong><span id="unpriced-count" class="stat-note"></span></div><div class="summary-stat"><span class="summary-label">Networks</span><strong id="chain-count">—</strong><span id="coverage-note" class="stat-note"></span></div></section>
|
|
53
|
+
<section id="discovery-notice" class="discovery-notice" aria-labelledby="discovery-notice-title" hidden><strong id="discovery-notice-title"></strong><p id="discovery-notice-text" role="status"></p><div class="discovery-actions"><button id="discovery-refresh" class="quiet-button" type="button" hidden>Refresh holdings</button><button id="discovery-settings" class="quiet-button" type="button" hidden>Connect token discovery</button></div><details><summary>Network coverage details</summary><p id="discovery-network-details"></p></details></section>
|
|
53
54
|
<section class="holdings" aria-labelledby="holdings-title"><div class="holdings-toolbar"><div><h2 id="holdings-title">Holdings <span id="visible-count"></span></h2></div><div class="filters"><label class="chain-select"><span class="sr-only">Filter by chain</span><select id="chain-filter"><option value="all">All chains</option></select><svg viewBox="0 0 16 16" aria-hidden="true"><path d="m4 6 4 4 4-4"/></svg></label><div class="segment" role="group" aria-label="Minimum token value"><button data-min="0" aria-pressed="true">All</button><button data-min="5" aria-pressed="false">≥ $5</button><button data-min="10" aria-pressed="false">≥ $10</button></div></div></div>
|
|
54
55
|
<div id="holdings-filter-note" class="holdings-filter-note" role="status" hidden></div><div id="asset-groups"></div>
|
|
55
56
|
<div id="empty" class="empty" hidden><div class="empty-mark" aria-hidden="true">∅</div><h3>No assets in this view</h3><p>Choose another chain or lower the minimum value.</p></div>
|
|
@@ -120,7 +121,8 @@
|
|
|
120
121
|
<label>Wallet name<input id="new-wallet-tag" required maxlength="200" placeholder="My research wallet" autocomplete="off"></label>
|
|
121
122
|
<section id="wallet-preview" class="wallet-preview" aria-label="Wallet registration preview" hidden><h3>Ready to watch</h3><dl><dt>Public address</dt><dd id="wallet-preview-address"></dd><dt>Exact wallet name</dt><dd id="wallet-preview-name"></dd></dl></section>
|
|
122
123
|
<p id="wallet-existing" class="wallet-existing" hidden>This Watching wallet is already saved. <a id="wallet-existing-link">Open the saved wallet</a> to keep its current names.</p>
|
|
123
|
-
<
|
|
124
|
+
<section id="wallet-discovery-warning" class="discovery-notice" aria-label="Token discovery connection"><strong id="wallet-discovery-warning-title">Checking token discovery</strong><p id="wallet-discovery-warning-text">An indexer is needed to find other ERC20 holdings. Public RPC alone checks native balances and Mint Club assets.</p><button id="wallet-discovery-settings" class="quiet-button" type="button">Connect token discovery</button></section>
|
|
125
|
+
<p class="form-note">Research can consume provider allowance. No signature is requested.</p>
|
|
124
126
|
<button id="wallet-register" class="primary-button" type="submit" disabled>Add and research</button>
|
|
125
127
|
</div>
|
|
126
128
|
<section id="wallet-submission" class="wallet-preview" aria-label="Submitted wallet request" hidden><h3 id="wallet-submission-title" role="status"></h3><dl><dt>Submitted public address</dt><dd id="wallet-submission-address"></dd><dt>Exact submitted name</dt><dd id="wallet-submission-name"></dd></dl><p id="wallet-submission-note" class="form-note"></p><div class="wallet-submission-actions"><button type="button" id="wallet-submission-activity" class="quiet-button">View Activity</button><button type="button" id="wallet-choose-again" class="quiet-button" disabled>Choose wallet again</button></div></section>
|
|
@@ -128,13 +130,14 @@
|
|
|
128
130
|
</form>
|
|
129
131
|
</dialog>
|
|
130
132
|
<dialog id="ows-dialog" class="research-dialog" aria-labelledby="ows-title">
|
|
131
|
-
<div class="dialog-heading"><h2 id="ows-title">Your local
|
|
133
|
+
<div class="dialog-heading"><h2 id="ows-title">Your local wallets</h2><button type="button" data-close-dialog aria-label="Close OWS wallets"><svg class="ui-icon" viewBox="0 0 24 24" fill="none" aria-hidden="true" focusable="false"><path d="m6 6 12 12M6 18 18 6"/></svg></button></div>
|
|
134
|
+
<section class="ows-provider-card" aria-label="Wallet creation with Open Wallet Standard"><div class="ows-provider-heading"><span>Powered by</span><a href="https://openwallet.sh/" target="_blank" rel="noreferrer"><img src="/ows-logo.svg" width="148" height="31" alt="OWS · Open Wallet Standard"><span aria-hidden="true">↗</span></a></div><h3>An encrypted wallet. On your computer.</h3><p>OWS creates your wallet and stores its keys in your local encrypted vault. Kira uses the public EVM account to research your holdings.</p></section>
|
|
132
135
|
<p id="ows-status" class="form-note" role="status">Checking the local vault…</p>
|
|
133
136
|
<div id="ows-pending" class="wallet-preview" hidden><p>A request is saved for recovery. Retry reuses a wallet already created and keeps its original encryption passphrase.</p><button id="ows-forget-request" type="button" class="text-button">I checked the vault. Start another request.</button></div>
|
|
134
137
|
<div id="ows-connection" class="wallet-preview" hidden><h3>Linked OWS account</h3><p id="ows-connected-name"></p><p id="ows-connected-address" class="contract-address"></p><button id="ows-disconnect" class="quiet-button" type="button">Disconnect from Kira</button></div>
|
|
135
138
|
<div id="ows-list" class="wallet-choices"></div>
|
|
136
139
|
<form id="ows-connect-form" hidden><h3 id="ows-selected-name"></h3><p id="ows-selected-address" class="contract-address"></p><label>Name in Kira<input id="ows-connect-tag" required maxlength="200" autocomplete="off"></label><button class="primary-button" type="submit">Link public account</button></form>
|
|
137
|
-
<details id="ows-create-section" open><summary>Create a new OWS wallet</summary><form id="ows-create-form"><p class="form-note">
|
|
140
|
+
<details id="ows-create-section" open><summary>Create a new OWS wallet</summary><form id="ows-create-form"><p class="form-note">Choose a passphrase only you know. Keep it safe, and use OWS backup tools for wallet recovery.</p><label>Wallet name<input id="ows-create-name" required minlength="2" maxlength="64" pattern="[A-Za-z0-9][A-Za-z0-9 ._\-]{1,63}" autocomplete="off"></label><label>Encryption passphrase<input id="ows-passphrase" type="password" required minlength="12" maxlength="1024" autocomplete="new-password"></label><label>Confirm passphrase<input id="ows-passphrase-confirm" type="password" required minlength="12" maxlength="1024" autocomplete="new-password"></label><button class="primary-button" type="submit">Create encrypted wallet</button></form></details>
|
|
138
141
|
<p id="ows-error" class="form-error" role="alert"></p>
|
|
139
142
|
</dialog>
|
|
140
143
|
<dialog id="tags-dialog" class="research-dialog" aria-labelledby="tags-title"><form id="tags-form"><div class="dialog-heading"><h2 id="tags-title">Wallet names</h2><button type="button" data-close-dialog aria-label="Close dialog"><svg class="ui-icon" viewBox="0 0 24 24" fill="none" aria-hidden="true" focusable="false"><path d="m6 6 12 12M6 18 18 6"/></svg></button></div><label>One exact name per line<textarea id="tag-values" required rows="4"></textarea></label><p class="form-note">The first name is shown in navigation. Saved analyses retain their original names.</p><p id="tags-error" class="form-error" role="alert"></p><button class="primary-button" type="submit">Save names</button></form></dialog>
|
package/viewer/static/jobs.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
let localSession=null, jobList=[], jobsSignature='', localPolling=false;
|
|
3
|
+
let discoverySettingsRevision='';
|
|
3
4
|
const pendingActions=new Set();
|
|
4
5
|
const jobLabels={'wallet.add':'Wallet research','wallet.refresh':'Holdings refresh','prices.refresh':'Price refresh','wallet.setTags':'Wallet names','settings.rpc':'RPC settings','settings.discovery':'Token discovery'};
|
|
5
6
|
const stageLabels={queued:'Waiting to start',starting:'Opening saved evidence',registered:'Wallet registered',discovery:'Token discovery',chain:'Checking on-chain holdings',dex_discovery:'Checking markets',token_images:'Preparing token artwork',published:'Evidence saved',failed:'Research stopped',stopped:'Research stopped',interrupted:'Ready for recovery'};
|
|
@@ -28,6 +29,7 @@ function refreshControlState(){
|
|
|
28
29
|
$(id).disabled=(sample&&id.startsWith('refresh'))||active||(id==='refresh-prices'&&!currentWallet()?.analysed_at);
|
|
29
30
|
$(id).title=sample?'Sample research cannot contact providers.':active?'A research job is already active for this wallet.':'';
|
|
30
31
|
}
|
|
32
|
+
$('discovery-refresh').disabled=$('refresh-wallet').disabled;
|
|
31
33
|
if(typeof renderWatching==='function')renderWatching();
|
|
32
34
|
}
|
|
33
35
|
let jobsPage=1, jobsConnected=true, lastJobsPoll=null;
|
|
@@ -74,13 +76,15 @@ async function pollJobs(){
|
|
|
74
76
|
if(!localSession){const response=await fetch('/api/session',{cache:'no-store',signal:AbortSignal.timeout(5000)});if(!response.ok)return;localSession=await response.json();}
|
|
75
77
|
refreshControlState();if(!localSession.controls){renderJobs();return;}
|
|
76
78
|
const jobs=await localAPI('/api/jobs');jobList=jobs;const reconnected=!jobsConnected;jobsConnected=true;lastJobsPoll=new Date().toISOString();
|
|
79
|
+
const setting=jobs.filter(j=>j.operation==='settings.discovery'&&j.state==='succeeded').map(j=>j.job_id+':'+j.updated_at).sort().join(',');
|
|
80
|
+
if(setting!==discoverySettingsRevision){discoverySettingsRevision=setting;if(typeof loadSetupReadiness==='function')await loadSetupReadiness();}
|
|
77
81
|
const signature=JSON.stringify(jobs)+JSON.stringify(jobs.map(j=>KiraView.jobPresentation(j).quiet));if(signature!==jobsSignature||reconnected){jobsSignature=signature;renderJobs();}else{$('jobs-freshness').textContent='Checked '+stamp(lastJobsPoll);}
|
|
78
82
|
refreshControlState();
|
|
79
83
|
}catch{jobsConnected=false;renderJobs();}
|
|
80
84
|
finally{localPolling=false;}
|
|
81
85
|
}
|
|
82
86
|
for(const button of document.querySelectorAll('[data-close-dialog]'))button.addEventListener('click',()=>button.closest('dialog').close());
|
|
83
|
-
function openAdd(){if(!localSession?.controls)return;if(!watchingSubmission)$('wallet-form-error').textContent='';if(typeof renderWatching==='function')renderWatching();$('wallet-dialog').showModal();}
|
|
87
|
+
function openAdd(){if(!localSession?.controls)return;if(!watchingSubmission)$('wallet-form-error').textContent='';if(typeof renderWatching==='function')renderWatching();$('wallet-dialog').showModal();if(typeof loadSetupReadiness==='function')loadSetupReadiness();}
|
|
84
88
|
$('open-wallet').addEventListener('click',openAdd);$('empty-add-wallet').addEventListener('click',openAdd);
|
|
85
89
|
function formAction(form,error,action){
|
|
86
90
|
form.addEventListener('submit',async e=>{e.preventDefault();error.textContent='';const buttons=[...form.querySelectorAll('button[type="submit"]')];if(buttons.some(b=>b.disabled))return;buttons.forEach(b=>b.disabled=true);
|
|
@@ -104,7 +108,10 @@ $('refresh-prices').addEventListener('click',async()=>{try{await submitOperation
|
|
|
104
108
|
$('edit-tags').addEventListener('click',()=>{if(!currentWallet())return;$('tags-dialog').dataset.wallet=selectedWallet;$('tag-values').value=currentWallet().tags.join('\n');$('tags-error').textContent='';$('tags-dialog').showModal();});
|
|
105
109
|
formAction($('tags-form'),$('tags-error'),async()=>{await submitOperation('wallet.setTags',{wallet:$('tags-dialog').dataset.wallet,tags:$('tag-values').value.split('\n').filter(t=>t.length)});$('tags-dialog').close();});
|
|
106
110
|
$('jobs-list').addEventListener('click',async event=>{const button=event.target.closest('[data-job-action]');if(!button)return;button.disabled=true;try{await submitOperation('job.'+button.dataset.jobAction,{job_id:button.dataset.jobId});}catch(error){button.disabled=false;toast(error.message);}});
|
|
107
|
-
$('open-settings').addEventListener('click',async()=>{try{const config=await localAPI('/api/settings');$('rpc-mode').value=config.rpc.mode;$('rpc-custom-only').checked=!config.rpc.allow_public_fallback;$('rpc-references').value=Object.entries(config.rpc.chains).map(([chain,row])=>chain+' '+row.url_env).join('\n');$('discovery-provider').value=config.discovery.provider;$('discovery-key').value=config.discovery.key_env;$('settings-error').textContent='';if(typeof KiraSelect!=='undefined')KiraSelect.refresh();$('settings-dialog').showModal();}catch(error){toast(error.message);}});
|
|
111
|
+
$('open-settings').addEventListener('click',async()=>{try{const config=await localAPI('/api/settings');$('rpc-mode').value=config.rpc.mode;$('rpc-custom-only').checked=!config.rpc.allow_public_fallback;$('rpc-references').value=Object.entries(config.rpc.chains).map(([chain,row])=>chain+' '+row.url_env).join('\n');$('discovery-provider').value=config.discovery.provider;$('discovery-key').value=config.discovery.key_env;$('settings-error').textContent='';if(typeof KiraSelect!=='undefined')KiraSelect.refresh();$('settings-dialog').showModal();if(typeof loadSetupReadiness==='function')loadSetupReadiness();}catch(error){toast(error.message);}});
|
|
112
|
+
$('discovery-settings').addEventListener('click',()=>$('open-settings').click());
|
|
113
|
+
$('wallet-discovery-settings').addEventListener('click',()=>{$('wallet-dialog').close();$('open-settings').click();});
|
|
114
|
+
$('discovery-refresh').addEventListener('click',()=>$('refresh-wallet').click());
|
|
108
115
|
formAction($('rpc-form'),$('settings-error'),async()=>{const lines=$('rpc-references').value.split('\n').filter(s=>s.trim());const chains=lines.map(line=>{const parts=line.trim().split(/\s+/);if(parts.length!==2)throw new Error('Enter a chain ID and environment variable name on each line.');return {chain_id:Number(parts[0]),url_env:parts[1]};});await submitOperation('settings.rpc',{mode:$('rpc-mode').value,allow_public_fallback:!$('rpc-custom-only').checked,chains});});
|
|
109
116
|
formAction($('discovery-form'),$('settings-error'),async()=>{await submitOperation('settings.discovery',{provider:$('discovery-provider').value,key_env:$('discovery-key').value});if(typeof loadSetupReadiness==='function')await loadSetupReadiness();});
|
|
110
117
|
$('view-history').addEventListener('click',async()=>{try{const rows=await localAPI('/api/snapshots?wallet='+encodeURIComponent(selectedWallet));$('history-list').innerHTML=rows.map(row=>`<article class="history-row"><strong>${stamp(row.compiled_at)}</strong><span>${escapeHTML(row.status==='completed'?'Saved':'Saved with coverage gaps')}</span><small>${escapeHTML(row.snapshot_id)}</small></article>`).join('')||'<p>No analysis has been published yet.</p>';$('compare-form').hidden=rows.length<2;for(const id of ['compare-before','compare-after'])$(id).innerHTML=rows.map(row=>`<option value="${escapeHTML(row.snapshot_id)}">${stamp(row.compiled_at)} · ${escapeHTML(row.snapshot_id.split('/').at(-1))}</option>`).join('');$('compare-after').selectedIndex=Math.max(0,rows.length-1);$('comparison-result').textContent='';$('history-error').textContent='';$('history-dialog').showModal();}catch(error){toast(error.message);}});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<svg width="235" height="49" viewBox="0 0 235 49" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M62.9134 16.7725C63.9577 16.7725 64.8969 16.9675 65.7251 17.3606C66.5532 17.7537 67.2554 18.2818 67.8315 18.948C68.4077 19.6141 68.8487 20.3913 69.1548 21.2795C69.4609 22.1677 69.6169 23.1099 69.6169 24.1031C69.6169 25.0963 69.4639 26.0566 69.1548 26.9448C68.8487 27.833 68.4077 28.6101 67.8315 29.2763C67.2554 29.9424 66.5532 30.4675 65.7251 30.8546C64.8939 31.2387 63.9577 31.4338 62.9134 31.4338C61.8692 31.4338 60.93 31.2417 60.1018 30.8546C59.2706 30.4705 58.5684 29.9424 57.9953 29.2763C57.4222 28.6101 56.9781 27.833 56.672 26.9448C56.3659 26.0566 56.2129 25.1083 56.2129 24.1031C56.2129 23.0979 56.3659 22.1707 56.672 21.2795C56.9781 20.3913 57.4192 19.6141 57.9953 18.948C58.5714 18.2818 59.2736 17.7507 60.1018 17.3606C60.93 16.9675 61.8692 16.7725 62.9134 16.7725ZM62.9134 18.7709C62.1693 18.7709 61.5241 18.9209 60.984 19.221C60.4409 19.5211 59.9997 19.9232 59.6607 20.4273C59.3216 20.9314 59.0696 21.5015 58.9045 22.1437C58.7425 22.7828 58.6584 23.437 58.6584 24.1031C58.6584 24.7693 58.7395 25.4234 58.9045 26.0626C59.0666 26.7017 59.3186 27.2748 59.6607 27.7789C59.9997 28.2831 60.4409 28.6851 60.984 28.9852C61.5271 29.2853 62.1693 29.4353 62.9134 29.4353C63.6576 29.4353 64.3027 29.2853 64.8428 28.9852C65.386 28.6851 65.8271 28.2831 66.1661 27.7789C66.5052 27.2748 66.7573 26.7047 66.9193 26.0626C67.0814 25.4234 67.1654 24.7693 67.1654 24.1031C67.1654 23.437 67.0844 22.7828 66.9193 22.1437C66.7573 21.5045 66.5052 20.9314 66.1661 20.4273C65.8271 19.9232 65.386 19.5241 64.8428 19.221C64.2997 18.9209 63.6576 18.7709 62.9134 18.7709Z" fill="white"/>
|
|
3
|
+
<path d="M70.6173 20.9674H72.7328V22.3387H72.7718C73.0839 21.7506 73.522 21.3305 74.0861 21.0754C74.6473 20.8204 75.2564 20.6943 75.9075 20.6943C76.7057 20.6943 77.3989 20.8354 77.996 21.1144C78.5902 21.3965 79.0853 21.7806 79.4753 22.2697C79.8684 22.7588 80.1625 23.3319 80.3576 23.9831C80.5526 24.6372 80.6516 25.3364 80.6516 26.0806C80.6516 26.7587 80.5646 27.4189 80.3876 28.061C80.2105 28.7001 79.9434 29.2673 79.5834 29.7564C79.2233 30.2455 78.7702 30.6386 78.2211 30.9326C77.672 31.2267 77.0268 31.3737 76.2796 31.3737C75.9525 31.3737 75.6255 31.3437 75.2984 31.2867C74.9713 31.2267 74.6593 31.1337 74.3562 31.0017C74.0561 30.8696 73.7771 30.7046 73.522 30.5005C73.2669 30.2995 73.0539 30.0594 72.8859 29.7864H72.8469V34.8425H70.6113V20.9644L70.6173 20.9674ZM78.4191 26.0415C78.4191 25.5854 78.3591 25.1383 78.2421 24.7092C78.125 24.2771 77.948 23.8961 77.7139 23.563C77.4799 23.2299 77.1858 22.9628 76.8318 22.7588C76.4777 22.5578 76.0726 22.4557 75.6165 22.4557C74.6743 22.4557 73.9661 22.7828 73.489 23.437C73.0119 24.0911 72.7748 24.9583 72.7748 26.0445C72.7748 26.5547 72.8379 27.0288 72.9609 27.4639C73.0839 27.902 73.2699 28.277 73.519 28.5921C73.7681 28.9072 74.0651 29.1532 74.4102 29.3363C74.7553 29.5193 75.1574 29.6093 75.6165 29.6093C76.1266 29.6093 76.5557 29.5043 76.9098 29.2973C77.2639 29.0872 77.5519 28.8172 77.783 28.4841C78.011 28.151 78.176 27.7729 78.2721 27.3468C78.3711 26.9207 78.4191 26.4886 78.4191 26.0445V26.0415Z" fill="white"/>
|
|
4
|
+
<path d="M83.6905 26.6117C83.6905 27.0048 83.7444 27.3829 83.8585 27.749C83.9695 28.1151 84.1346 28.4362 84.3596 28.7092C84.5817 28.9823 84.8637 29.2013 85.2028 29.3664C85.5418 29.5284 85.947 29.6124 86.4181 29.6124C87.0722 29.6124 87.5973 29.4714 87.9964 29.1923C88.3955 28.9103 88.6926 28.4902 88.8876 27.929H91.0031C90.8861 28.4782 90.682 28.9673 90.397 29.3994C90.1089 29.8315 89.7638 30.1946 89.3587 30.4856C88.9536 30.7797 88.5005 31.0017 87.9964 31.1518C87.4923 31.3018 86.9672 31.3768 86.4181 31.3768C85.6199 31.3768 84.9147 31.2448 84.3026 30.9837C83.6874 30.7227 83.1683 30.3566 82.7452 29.8855C82.3191 29.4144 82.0011 28.8532 81.785 28.1991C81.569 27.545 81.4609 26.8278 81.4609 26.0446C81.4609 25.3275 81.575 24.6433 81.803 23.9952C82.0311 23.347 82.3582 22.7799 82.7842 22.2908C83.2073 21.8017 83.7205 21.4116 84.3236 21.1235C84.9237 20.8354 85.6049 20.6914 86.3611 20.6914C87.1592 20.6914 87.8734 20.8564 88.5065 21.1925C89.1397 21.5286 89.6648 21.9667 90.0849 22.5158C90.502 23.0649 90.8081 23.6951 90.9971 24.4062C91.1861 25.1204 91.2341 25.8526 91.1441 26.6117H83.6965H83.6905ZM88.9026 25.1414C88.8756 24.7873 88.8006 24.4483 88.6776 24.1212C88.5546 23.7941 88.3835 23.509 88.1674 23.269C87.9514 23.0259 87.6903 22.8309 87.3843 22.6809C87.0782 22.5308 86.7331 22.4558 86.3551 22.4558C85.977 22.4558 85.6079 22.5248 85.2868 22.6629C84.9657 22.8009 84.6927 22.9899 84.4646 23.23C84.2366 23.473 84.0535 23.7551 83.9155 24.0822C83.7775 24.4092 83.7025 24.7603 83.6905 25.1414H88.9056H88.9026Z" fill="white"/>
|
|
5
|
+
<path d="M92.2551 20.9675H94.3706V22.4558L94.4096 22.4948C94.7486 21.9337 95.1927 21.4926 95.7419 21.1715C96.291 20.8504 96.8971 20.6914 97.5633 20.6914C98.6735 20.6914 99.5497 20.9795 100.189 21.5526C100.828 22.1287 101.149 22.9899 101.149 24.1392V31.0978H98.9136V24.7273C98.8866 23.9291 98.7186 23.353 98.4035 22.9929C98.0914 22.6328 97.5993 22.4558 96.9331 22.4558C96.5551 22.4558 96.213 22.5248 95.9129 22.6629C95.6128 22.8009 95.3578 22.9899 95.1477 23.23C94.9377 23.473 94.7756 23.7551 94.6586 24.0822C94.5416 24.4092 94.4816 24.7543 94.4816 25.1204V31.0978H92.2461V20.9645L92.2551 20.9675Z" fill="white"/>
|
|
6
|
+
<path d="M103.975 17.1055H106.465L108.995 27.8059H109.034L111.857 17.1055H114.366L117.109 27.8059H117.148L119.755 17.1055H122.246L118.384 31.1006H115.896L113.094 20.4002H113.055L110.192 31.1006H107.662L103.978 17.1055H103.975Z" fill="white"/>
|
|
7
|
+
<path d="M130.318 28.844C130.318 29.1171 130.354 29.3151 130.426 29.4322C130.498 29.5492 130.639 29.6092 130.847 29.6092H131.081C131.171 29.6092 131.276 29.5972 131.393 29.5702V31.1185C131.315 31.1456 131.213 31.1726 131.09 31.2056C130.964 31.2386 130.837 31.2686 130.708 31.2926C130.576 31.3196 130.447 31.3376 130.315 31.3526C130.183 31.3646 130.072 31.3706 129.982 31.3706C129.523 31.3706 129.145 31.2806 128.845 31.0975C128.545 30.9145 128.347 30.5934 128.257 30.1373C127.813 30.5694 127.267 30.8815 126.622 31.0795C125.976 31.2776 125.349 31.3736 124.749 31.3736C124.293 31.3736 123.855 31.3106 123.435 31.1876C123.018 31.0645 122.649 30.8815 122.328 30.6384C122.007 30.3984 121.751 30.0893 121.562 29.7172C121.373 29.3451 121.277 28.91 121.277 28.4149C121.277 27.7878 121.391 27.2777 121.619 26.8876C121.847 26.4945 122.148 26.1884 122.52 25.9664C122.892 25.7443 123.309 25.5853 123.774 25.4863C124.239 25.3873 124.704 25.3122 125.175 25.2612C125.58 25.1832 125.964 25.1262 126.33 25.0962C126.697 25.0632 127.021 25.0092 127.3 24.9281C127.582 24.8501 127.804 24.7301 127.966 24.5651C128.128 24.403 128.212 24.157 128.212 23.8299C128.212 23.5418 128.143 23.3078 128.005 23.1247C127.867 22.9417 127.699 22.8007 127.495 22.7046C127.291 22.6056 127.066 22.5426 126.82 22.5096C126.571 22.4766 126.336 22.4616 126.114 22.4616C125.487 22.4616 124.971 22.5936 124.566 22.8547C124.161 23.1157 123.933 23.5208 123.879 24.07H121.643C121.682 23.4158 121.838 22.8757 122.115 22.4436C122.391 22.0115 122.739 21.6664 123.165 21.4054C123.588 21.1443 124.068 20.9613 124.605 20.8562C125.142 20.7512 125.688 20.7002 126.252 20.7002C126.748 20.7002 127.24 20.7512 127.723 20.8562C128.206 20.9613 128.641 21.1323 129.025 21.3663C129.409 21.6004 129.721 21.9065 129.955 22.2786C130.189 22.6506 130.309 23.1037 130.309 23.6409V28.856L130.318 28.844ZM128.083 26.0234C127.744 26.2454 127.324 26.3805 126.829 26.4255C126.33 26.4705 125.835 26.5395 125.34 26.6325C125.106 26.6715 124.875 26.7286 124.653 26.7976C124.431 26.8696 124.236 26.9686 124.065 27.0916C123.894 27.2147 123.762 27.3797 123.663 27.5807C123.564 27.7848 123.516 28.0278 123.516 28.3159C123.516 28.565 123.588 28.775 123.732 28.9431C123.876 29.1141 124.05 29.2461 124.251 29.3451C124.452 29.4442 124.674 29.5102 124.917 29.5522C125.157 29.5912 125.379 29.6122 125.574 29.6122C125.823 29.6122 126.09 29.5792 126.378 29.5132C126.667 29.4472 126.937 29.3361 127.192 29.1801C127.447 29.0241 127.66 28.823 127.828 28.583C127.999 28.3399 128.083 28.0429 128.083 27.6918V26.0264V26.0234Z" fill="white"/>
|
|
8
|
+
<path d="M132.199 17.1055H134.435V31.1006H132.199V17.1055Z" fill="white"/>
|
|
9
|
+
<path d="M135.941 17.1055H138.177V31.1006H135.941V17.1055Z" fill="white"/>
|
|
10
|
+
<path d="M141.372 26.6117C141.372 27.0048 141.426 27.3829 141.54 27.749C141.651 28.1151 141.816 28.4362 142.041 28.7092C142.263 28.9823 142.545 29.2013 142.884 29.3664C143.224 29.5284 143.629 29.6124 144.1 29.6124C144.754 29.6124 145.279 29.4714 145.678 29.1923C146.077 28.9103 146.374 28.4902 146.569 27.929H148.685C148.568 28.4782 148.364 28.9673 148.079 29.3994C147.791 29.8315 147.445 30.1946 147.04 30.4856C146.635 30.7797 146.182 31.0017 145.678 31.1518C145.174 31.3018 144.649 31.3768 144.1 31.3768C143.302 31.3768 142.596 31.2448 141.984 30.9837C141.369 30.7227 140.85 30.3566 140.427 29.8855C140.001 29.4144 139.683 28.8532 139.467 28.1991C139.251 27.545 139.143 26.8278 139.143 26.0446C139.143 25.3275 139.257 24.6433 139.485 23.9952C139.713 23.347 140.04 22.7799 140.466 22.2908C140.889 21.8017 141.402 21.4116 142.005 21.1235C142.605 20.8354 143.287 20.6914 144.043 20.6914C144.841 20.6914 145.555 20.8564 146.188 21.1925C146.821 21.5286 147.346 21.9667 147.767 22.5158C148.184 23.0649 148.49 23.6951 148.679 24.4062C148.868 25.1204 148.916 25.8526 148.826 26.6117H141.378H141.372ZM146.584 25.1414C146.557 24.7873 146.482 24.4483 146.359 24.1212C146.236 23.7941 146.065 23.509 145.849 23.269C145.633 23.0259 145.372 22.8309 145.066 22.6809C144.76 22.5308 144.415 22.4558 144.037 22.4558C143.659 22.4558 143.29 22.5248 142.968 22.6629C142.647 22.8009 142.374 22.9899 142.146 23.23C141.918 23.473 141.735 23.7551 141.597 24.0822C141.459 24.4092 141.384 24.7603 141.372 25.1414H146.587H146.584Z" fill="white"/>
|
|
11
|
+
<path d="M148.916 20.9673H150.602V17.9307H152.838V20.9673H154.857V22.6327H152.838V28.0429C152.838 28.277 152.847 28.481 152.868 28.6521C152.886 28.8231 152.934 28.9642 153.006 29.0842C153.078 29.2012 153.186 29.2912 153.33 29.3482C153.474 29.4083 153.669 29.4353 153.918 29.4353C154.074 29.4353 154.23 29.4353 154.389 29.4263C154.548 29.4173 154.701 29.3962 154.86 29.3572V31.0826C154.611 31.1096 154.371 31.1336 154.134 31.1606C153.9 31.1877 153.657 31.1997 153.408 31.1997C152.82 31.1997 152.346 31.1426 151.986 31.0346C151.626 30.9236 151.347 30.7616 151.142 30.5455C150.938 30.3295 150.803 30.0594 150.731 29.7323C150.659 29.4053 150.617 29.0332 150.605 28.6161V22.6387H148.919V20.9734L148.916 20.9673Z" fill="white"/>
|
|
12
|
+
<path d="M160.698 26.4348C160.698 26.9719 160.788 27.428 160.971 27.8061C161.154 28.1842 161.409 28.4962 161.736 28.7363C162.063 28.9794 162.438 29.1534 162.864 29.2644C163.287 29.3754 163.749 29.4295 164.244 29.4295C164.781 29.4295 165.238 29.3664 165.616 29.2434C165.994 29.1204 166.303 28.9583 166.537 28.7633C166.771 28.5683 166.942 28.3432 167.047 28.0882C167.152 27.8331 167.203 27.575 167.203 27.314C167.203 26.7769 167.083 26.3838 166.84 26.1287C166.597 25.8737 166.333 25.6936 166.045 25.5886C165.547 25.4055 164.97 25.2315 164.31 25.0695C163.65 24.9074 162.837 24.6944 161.871 24.4333C161.271 24.2773 160.77 24.0702 160.37 23.8152C159.971 23.5601 159.656 23.2751 159.419 22.963C159.185 22.6509 159.017 22.3149 158.918 21.9638C158.819 21.6097 158.771 21.2526 158.771 20.8865C158.771 20.1814 158.918 19.5692 159.212 19.0531C159.506 18.537 159.896 18.1109 160.379 17.7688C160.863 17.4298 161.412 17.1777 162.027 17.0127C162.642 16.8506 163.263 16.7666 163.89 16.7666C164.622 16.7666 165.313 16.8626 165.958 17.0517C166.606 17.2407 167.173 17.5258 167.662 17.9039C168.151 18.2819 168.541 18.75 168.829 19.3052C169.117 19.8603 169.261 20.5054 169.261 21.2346H166.81C166.744 20.3344 166.438 19.6953 165.889 19.3232C165.34 18.9511 164.64 18.7651 163.791 18.7651C163.503 18.7651 163.209 18.7951 162.909 18.8521C162.609 18.9121 162.333 19.0081 162.087 19.1461C161.838 19.2842 161.634 19.4702 161.469 19.7043C161.307 19.9383 161.223 20.2324 161.223 20.5865C161.223 21.0846 161.376 21.4717 161.685 21.7537C161.991 22.0358 162.393 22.2458 162.891 22.3899C162.942 22.4019 163.149 22.4589 163.509 22.5579C163.869 22.6569 164.271 22.765 164.715 22.882C165.16 22.999 165.595 23.113 166.018 23.2241C166.441 23.3351 166.747 23.4161 166.93 23.4701C167.386 23.6141 167.785 23.8092 168.124 24.0582C168.463 24.3073 168.748 24.5924 168.976 24.9104C169.204 25.2285 169.375 25.5736 169.486 25.9397C169.597 26.3058 169.655 26.6718 169.655 27.0379C169.655 27.8211 169.495 28.4932 169.174 29.0484C168.853 29.6035 168.433 30.0566 167.911 30.4107C167.389 30.7648 166.795 31.0228 166.129 31.1849C165.463 31.3469 164.781 31.4309 164.091 31.4309C163.293 31.4309 162.543 31.3319 161.838 31.1368C161.133 30.9418 160.517 30.6417 159.995 30.2366C159.473 29.8315 159.056 29.3124 158.741 28.6793C158.429 28.0461 158.264 27.296 158.252 26.4348H160.704H160.698Z" fill="white"/>
|
|
13
|
+
<path d="M169.871 20.9673H171.557V17.9307H173.793V20.9673H175.812V22.6327H173.793V28.0429C173.793 28.277 173.802 28.481 173.823 28.6521C173.841 28.8231 173.889 28.9642 173.961 29.0842C174.033 29.2012 174.141 29.2912 174.285 29.3482C174.429 29.4083 174.624 29.4353 174.873 29.4353C175.029 29.4353 175.185 29.4353 175.344 29.4263C175.503 29.4173 175.656 29.3962 175.815 29.3572V31.0826C175.566 31.1096 175.326 31.1336 175.089 31.1606C174.855 31.1877 174.612 31.1997 174.363 31.1997C173.775 31.1997 173.301 31.1426 172.941 31.0346C172.581 30.9236 172.302 30.7616 172.098 30.5455C171.894 30.3295 171.759 30.0594 171.687 29.7323C171.615 29.4053 171.572 29.0332 171.56 28.6161V22.6387H169.874V20.9734L169.871 20.9673Z" fill="white"/>
|
|
14
|
+
<path d="M185.314 28.844C185.314 29.1171 185.351 29.3151 185.423 29.4322C185.495 29.5492 185.636 29.6092 185.843 29.6092H186.077C186.167 29.6092 186.272 29.5972 186.389 29.5702V31.1185C186.311 31.1456 186.209 31.1726 186.086 31.2056C185.96 31.2386 185.834 31.2686 185.705 31.2926C185.573 31.3196 185.444 31.3376 185.311 31.3526C185.179 31.3646 185.068 31.3706 184.978 31.3706C184.519 31.3706 184.141 31.2806 183.841 31.0975C183.541 30.9145 183.343 30.5934 183.253 30.1373C182.809 30.5694 182.263 30.8815 181.618 31.0795C180.973 31.2776 180.345 31.3736 179.745 31.3736C179.289 31.3736 178.851 31.3106 178.431 31.1876C178.014 31.0645 177.645 30.8815 177.324 30.6384C177.003 30.3984 176.748 30.0893 176.559 29.7172C176.369 29.3451 176.273 28.91 176.273 28.4149C176.273 27.7878 176.387 27.2777 176.616 26.8876C176.844 26.4945 177.144 26.1884 177.516 25.9664C177.888 25.7443 178.305 25.5853 178.77 25.4863C179.235 25.3873 179.7 25.3122 180.171 25.2612C180.576 25.1832 180.961 25.1262 181.327 25.0962C181.693 25.0632 182.017 25.0092 182.296 24.9281C182.578 24.8501 182.8 24.7301 182.962 24.5651C183.124 24.403 183.208 24.157 183.208 23.8299C183.208 23.5418 183.139 23.3078 183.001 23.1247C182.863 22.9417 182.695 22.8007 182.491 22.7046C182.287 22.6056 182.062 22.5426 181.816 22.5096C181.567 22.4766 181.333 22.4616 181.111 22.4616C180.483 22.4616 179.967 22.5936 179.562 22.8547C179.157 23.1157 178.929 23.5208 178.875 24.07H176.64C176.679 23.4158 176.835 22.8757 177.111 22.4436C177.387 22.0115 177.735 21.6664 178.161 21.4054C178.584 21.1443 179.064 20.9613 179.601 20.8562C180.138 20.7512 180.684 20.7002 181.249 20.7002C181.744 20.7002 182.236 20.7512 182.719 20.8562C183.202 20.9613 183.637 21.1323 184.021 21.3663C184.405 21.6004 184.717 21.9065 184.951 22.2786C185.185 22.6506 185.306 23.1037 185.306 23.6409V28.856L185.314 28.844ZM183.079 26.0234C182.74 26.2454 182.32 26.3805 181.825 26.4255C181.327 26.4705 180.831 26.5395 180.336 26.6325C180.102 26.6715 179.871 26.7286 179.649 26.7976C179.427 26.8696 179.232 26.9686 179.061 27.0916C178.89 27.2147 178.758 27.3797 178.659 27.5807C178.56 27.7848 178.512 28.0278 178.512 28.3159C178.512 28.565 178.584 28.775 178.728 28.9431C178.872 29.1141 179.046 29.2461 179.247 29.3451C179.448 29.4442 179.67 29.5102 179.913 29.5522C180.153 29.5912 180.375 29.6122 180.57 29.6122C180.819 29.6122 181.087 29.5792 181.375 29.5132C181.663 29.4472 181.933 29.3361 182.188 29.1801C182.443 29.0241 182.656 28.823 182.824 28.583C182.995 28.3399 183.079 28.0429 183.079 27.6918V26.0264V26.0234Z" fill="white"/>
|
|
15
|
+
<path d="M187.136 20.9675H189.251V22.4558L189.29 22.4948C189.63 21.9337 190.074 21.4926 190.623 21.1715C191.172 20.8504 191.778 20.6914 192.444 20.6914C193.554 20.6914 194.431 20.9795 195.07 21.5526C195.709 22.1287 196.03 22.9899 196.03 24.1392V31.0978H193.794V24.7273C193.767 23.9291 193.599 23.353 193.284 22.9929C192.972 22.6328 192.48 22.4558 191.814 22.4558C191.436 22.4558 191.094 22.5248 190.794 22.6629C190.494 22.8009 190.239 22.9899 190.029 23.23C189.819 23.473 189.657 23.7551 189.54 24.0822C189.422 24.4092 189.362 24.7543 189.362 25.1204V31.0978H187.127V20.9645L187.136 20.9675Z" fill="white"/>
|
|
16
|
+
<path d="M207.346 31.0977H205.23V29.7264H205.191C204.891 30.3145 204.453 30.7346 203.877 30.9897C203.301 31.2447 202.695 31.3708 202.053 31.3708C201.254 31.3708 200.561 31.2297 199.964 30.9507C199.37 30.6686 198.878 30.2875 198.485 29.8044C198.092 29.3213 197.798 28.7482 197.603 28.088C197.408 27.4279 197.309 26.7197 197.309 25.9605C197.309 25.0453 197.432 24.2562 197.681 23.59C197.93 22.9239 198.26 22.3747 198.671 21.9426C199.082 21.5105 199.553 21.1955 200.081 20.9914C200.609 20.7904 201.149 20.6883 201.699 20.6883C202.014 20.6883 202.332 20.7184 202.659 20.7754C202.986 20.8354 203.298 20.9284 203.598 21.0604C203.898 21.1925 204.177 21.3575 204.432 21.5615C204.687 21.7656 204.9 22.0026 205.068 22.2757H205.107V17.1025H207.343V31.0977H207.346ZM199.544 26.1406C199.544 26.5727 199.598 26.9958 199.712 27.4159C199.823 27.833 199.994 28.205 200.222 28.5321C200.45 28.8592 200.741 29.1202 201.095 29.3153C201.45 29.5103 201.867 29.6094 202.35 29.6094C202.833 29.6094 203.274 29.5043 203.634 29.2973C203.994 29.0872 204.288 28.8142 204.516 28.4751C204.744 28.136 204.915 27.7519 205.026 27.3288C205.137 26.9057 205.191 26.4707 205.191 26.0266C205.191 24.9043 204.939 24.0281 204.438 23.401C203.934 22.7738 203.253 22.4617 202.389 22.4617C201.867 22.4617 201.425 22.5698 201.065 22.7858C200.705 23.0019 200.411 23.2839 200.183 23.629C199.955 23.9741 199.79 24.3672 199.694 24.8053C199.595 25.2434 199.547 25.6905 199.547 26.1466L199.544 26.1406Z" fill="white"/>
|
|
17
|
+
<path d="M217.674 28.844C217.674 29.1171 217.71 29.3151 217.782 29.4322C217.854 29.5492 217.995 29.6092 218.202 29.6092H218.436C218.526 29.6092 218.631 29.5972 218.748 29.5702V31.1185C218.67 31.1456 218.568 31.1726 218.445 31.2056C218.319 31.2386 218.193 31.2686 218.064 31.2926C217.932 31.3196 217.803 31.3376 217.671 31.3526C217.539 31.3646 217.428 31.3706 217.338 31.3706C216.879 31.3706 216.501 31.2806 216.201 31.0975C215.9 30.9145 215.702 30.5934 215.612 30.1373C215.168 30.5694 214.622 30.8815 213.977 31.0795C213.332 31.2776 212.705 31.3736 212.105 31.3736C211.648 31.3736 211.21 31.3106 210.79 31.1876C210.373 31.0645 210.004 30.8815 209.683 30.6384C209.362 30.3984 209.107 30.0893 208.918 29.7172C208.729 29.3451 208.633 28.91 208.633 28.4149C208.633 27.7878 208.747 27.2777 208.975 26.8876C209.203 26.4945 209.503 26.1884 209.875 25.9664C210.247 25.7443 210.664 25.5853 211.129 25.4863C211.594 25.3873 212.06 25.3122 212.531 25.2612C212.936 25.1832 213.32 25.1262 213.686 25.0962C214.052 25.0632 214.376 25.0092 214.655 24.9281C214.937 24.8501 215.159 24.7301 215.321 24.5651C215.483 24.403 215.567 24.157 215.567 23.8299C215.567 23.5418 215.498 23.3078 215.36 23.1247C215.222 22.9417 215.054 22.8007 214.85 22.7046C214.646 22.6056 214.421 22.5426 214.175 22.5096C213.926 22.4766 213.692 22.4616 213.47 22.4616C212.843 22.4616 212.327 22.5936 211.922 22.8547C211.516 23.1157 211.288 23.5208 211.234 24.07H208.999C209.038 23.4158 209.194 22.8757 209.47 22.4436C209.746 22.0115 210.094 21.6664 210.52 21.4054C210.943 21.1443 211.423 20.9613 211.961 20.8562C212.498 20.7512 213.044 20.7002 213.608 20.7002C214.103 20.7002 214.595 20.7512 215.078 20.8562C215.561 20.9613 215.996 21.1323 216.381 21.3663C216.765 21.6004 217.077 21.9065 217.311 22.2786C217.545 22.6506 217.665 23.1037 217.665 23.6409V28.856L217.674 28.844ZM215.438 26.0234C215.099 26.2454 214.679 26.3805 214.184 26.4255C213.686 26.4705 213.191 26.5395 212.696 26.6325C212.462 26.6715 212.231 26.7286 212.009 26.7976C211.787 26.8696 211.591 26.9686 211.42 27.0916C211.249 27.2147 211.117 27.3797 211.018 27.5807C210.919 27.7848 210.871 28.0278 210.871 28.3159C210.871 28.565 210.943 28.775 211.087 28.9431C211.231 29.1141 211.405 29.2461 211.606 29.3451C211.808 29.4442 212.03 29.5102 212.273 29.5522C212.513 29.5912 212.735 29.6122 212.93 29.6122C213.179 29.6122 213.446 29.5792 213.734 29.5132C214.022 29.4472 214.292 29.3361 214.547 29.1801C214.802 29.0241 215.015 28.823 215.183 28.583C215.354 28.3399 215.438 28.0429 215.438 27.6918V26.0264V26.0234Z" fill="white"/>
|
|
18
|
+
<path d="M219.495 20.9674H221.593V22.9268H221.632C221.698 22.6538 221.824 22.3837 222.013 22.1227C222.202 21.8616 222.43 21.6215 222.7 21.4085C222.967 21.1924 223.264 21.0184 223.591 20.8894C223.918 20.7604 224.251 20.6943 224.59 20.6943C224.851 20.6943 225.031 20.7003 225.13 20.7123C225.229 20.7243 225.329 20.7393 225.434 20.7513V22.9088C225.278 22.8818 225.115 22.8608 224.953 22.8398C224.791 22.8188 224.629 22.8098 224.473 22.8098C224.095 22.8098 223.738 22.8848 223.405 23.0349C223.072 23.1849 222.781 23.4069 222.532 23.701C222.283 23.9951 222.088 24.3582 221.944 24.7873C221.8 25.2194 221.728 25.7145 221.728 26.2756V31.0977H219.492V20.9644L219.495 20.9674Z" fill="white"/>
|
|
19
|
+
<path d="M235 31.0977H232.885V29.7264H232.846C232.546 30.3145 232.108 30.7346 231.531 30.9897C230.955 31.2447 230.349 31.3708 229.707 31.3708C228.909 31.3708 228.216 31.2297 227.619 30.9507C227.024 30.6686 226.532 30.2875 226.139 29.8044C225.746 29.3213 225.452 28.7482 225.257 28.088C225.062 27.4279 224.963 26.7197 224.963 25.9605C224.963 25.0453 225.086 24.2562 225.335 23.59C225.584 22.9239 225.914 22.3747 226.325 21.9426C226.736 21.5105 227.207 21.1955 227.736 20.9914C228.264 20.7904 228.804 20.6883 229.353 20.6883C229.668 20.6883 229.986 20.7184 230.313 20.7754C230.64 20.8354 230.952 20.9284 231.252 21.0604C231.552 21.1925 231.831 21.3575 232.086 21.5615C232.342 21.7656 232.555 22.0026 232.723 22.2757H232.762V17.1025H234.997V31.0977H235ZM227.198 26.1406C227.198 26.5727 227.252 26.9958 227.366 27.4159C227.477 27.833 227.649 28.205 227.877 28.5321C228.105 28.8592 228.396 29.1202 228.75 29.3153C229.104 29.5103 229.521 29.6094 230.004 29.6094C230.487 29.6094 230.928 29.5043 231.288 29.2973C231.648 29.0872 231.942 28.8142 232.171 28.4751C232.399 28.136 232.57 27.7519 232.681 27.3288C232.792 26.9057 232.846 26.4707 232.846 26.0266C232.846 24.9043 232.594 24.0281 232.093 23.401C231.588 22.7738 230.907 22.4617 230.043 22.4617C229.521 22.4617 229.08 22.5698 228.72 22.7858C228.36 23.0019 228.066 23.2839 227.838 23.629C227.61 23.9741 227.444 24.3672 227.348 24.8053C227.249 25.2434 227.201 25.6905 227.201 26.1466L227.198 26.1406Z" fill="white"/>
|
|
20
|
+
<path d="M9.89453 3.39258L17.6757 11.1738L24.296 4.55349L30.9163 11.1738L38.6975 3.39258" stroke="white" stroke-width="2.40025"/>
|
|
21
|
+
<path d="M45.3047 9.99902L37.5235 17.7802L44.1438 24.4005L37.5235 31.0208L45.3047 38.802" stroke="white" stroke-width="2.40025"/>
|
|
22
|
+
<path d="M38.6973 45.4082L30.9161 37.627L24.2958 44.2473L17.6755 37.627L9.8943 45.4082" stroke="white" stroke-width="2.40025"/>
|
|
23
|
+
<path d="M3.28906 38.8018L11.0703 31.0206L4.44998 24.4003L11.0703 17.78L3.28907 9.99879" stroke="white" stroke-width="2.40025"/>
|
|
24
|
+
</svg>
|
package/viewer/static/report.css
CHANGED
|
@@ -105,6 +105,10 @@ body.page-hidden .kira-scene img{animation:none!important}
|
|
|
105
105
|
#ows-dialog .wallet-choices { margin: 18px 0; }
|
|
106
106
|
#ows-create-section { margin-top: 22px; border-top: 1px solid var(--line); padding-top: 16px; }
|
|
107
107
|
#ows-create-section summary { min-height: 44px; color: var(--accent); cursor: pointer; }
|
|
108
|
+
#ows-list:empty { display: none; }
|
|
109
|
+
#ows-dialog #ows-create-section { margin-top: 10px; padding-top: 10px; }
|
|
110
|
+
#ows-create-form label { margin: 10px 0 8px; }
|
|
111
|
+
#ows-create-form .form-note { margin: 8px 0; }
|
|
108
112
|
#ows-connect-form { margin-top: 16px; }
|
|
109
113
|
.wallet-unavailable { padding: 16px; border: 1px solid var(--line); border-radius: 12px; }
|
|
110
114
|
.wallet-unavailable h3 { margin: 0 0 8px; }
|
package/viewer/static/report.js
CHANGED
|
@@ -15,7 +15,7 @@ function renderSetupPath() {
|
|
|
15
15
|
const discovery=setupReadiness?.discovery_key_available===true,configured=setupReadiness?.discovery_configured===true,ai=typeof agentState!=='undefined'&&!!agentState?.config;
|
|
16
16
|
$('setup-path').hidden=state.demo||!localSession?.controls||(wallet&&analysed&&discovery&&ai);
|
|
17
17
|
$('home-empty').hidden=wallet||!$('setup-path').hidden;
|
|
18
|
-
$('setup-path').innerHTML='<details '+(!wallet?'open':'')+'><summary>'+ (wallet?'Finish your setup':'Set up your first wallet')+'</summary><div class="section-heading"><h2>'+ (wallet?'Finish your setup':'Start with one wallet')+'</h2><span class="section-note">Your keys stay yours</span></div><ol class="setup-checklist">'+
|
|
18
|
+
$('setup-path').innerHTML='<details '+(!wallet||!discovery?'open':'')+'><summary>'+ (wallet?'Finish your setup':'Set up your first wallet')+'</summary><div class="section-heading"><h2>'+ (wallet?'Finish your setup':'Start with one wallet')+'</h2><span class="section-note">Your keys stay yours</span></div><ol class="setup-checklist">'+
|
|
19
19
|
'<li><span class="setup-check">'+(wallet?'✓':'1')+'</span><div><strong>Add a public wallet</strong><p>'+(wallet?'Saved locally.':'Paste an EVM address or choose a browser wallet. No signature or seed phrase.')+'</p></div><button class="quiet-button" id="setup-wallet" type="button">'+(wallet?'Add another':'Add wallet')+'</button></li>'+
|
|
20
20
|
'<li><span class="setup-check">'+(discovery?'✓':'2')+'</span><div><strong>Connect token discovery</strong><p>'+(!setupReadiness?(setupUnavailable?'Could not check the connection. Retrying…':'Checking the discovery connection…'):discovery?'Indexer key detected. Actual chain coverage is confirmed by research results.':configured?'Indexer selected, but its key is unavailable. Use a private environment reference.':'Public RPC checks known assets. Connect an indexer for broader ERC20 discovery, or continue with limited coverage.')+'</p></div><button class="quiet-button" id="setup-discovery" type="button">Review connection</button></li>'+
|
|
21
21
|
'<li><span class="setup-check">'+(analysed?'✓':'3')+'</span><div><strong>Read your first report</strong><p>'+(analysed?'Saved balances, prices and coverage are ready.':'Adding a wallet starts one explicit research job. Follow its progress in Activity; gaps are shown in the report.')+'</p></div><a class="quiet-button" href="#/activity">View Activity</a></li>'+
|
|
@@ -26,7 +26,7 @@ function renderSetupPath() {
|
|
|
26
26
|
}
|
|
27
27
|
async function loadSetupReadiness(){
|
|
28
28
|
if(setupLoading||!localSession?.controls)return false;setupLoading=true;
|
|
29
|
-
try{setupReadiness=await localAPI('/api/onboarding');setupUnavailable=false;setupRetryAfter=0;renderSetupPath();return true;}catch{setupReadiness=null;setupUnavailable=true;setupRetryAfter=Date.now()+5000;renderSetupPath();return false;}finally{setupLoading=false;}
|
|
29
|
+
try{setupReadiness=await localAPI('/api/onboarding');setupUnavailable=false;setupRetryAfter=0;renderSetupPath();if(typeof renderWatching==='function')renderWatching();if(typeof currentWallet==='function'&¤tWallet())renderDiscoveryCoverage(currentWallet());return true;}catch{setupReadiness=null;setupUnavailable=true;setupRetryAfter=Date.now()+5000;renderSetupPath();return false;}finally{setupLoading=false;}
|
|
30
30
|
}
|
|
31
31
|
window.addEventListener('hashchange',()=>{if(selectedView==='home')renderHomeTokens();});
|
|
32
32
|
const setupStartup=setInterval(async()=>{if(state&&localSession?.controls&&!setupReadiness&&Date.now()>=setupRetryAfter){await loadSetupReadiness();renderHomeTokens();}},1000);
|
package/viewer/static/style.css
CHANGED
|
@@ -49,3 +49,5 @@ body{background:#fcfbf8;color:#292733}main{background:#fcfbf8}.sidebar{backgroun
|
|
|
49
49
|
.brand-logo,.nav-logo,.note-logo{object-fit:contain;flex-shrink:0}.brand-logo{width:46px;height:46px}.nav-logo{width:28px;height:28px}.note-logo{width:42px;height:42px}.research-controls{display:flex;align-items:center;justify-content:space-between;gap:16px;margin:0 0 26px}.research-controls p{font-size:12px;color:#686174;margin:6px 0 0}.control-buttons,.wallet-actions{display:flex;flex-wrap:wrap;gap:9px}.wallet-actions{justify-content:flex-end;margin:0 0 22px}.primary-button,.quiet-button{font:inherit;font-size:12px;font-weight:600;min-height:40px;padding:10px 15px;border-radius:10px;cursor:pointer}.primary-button{background:#6755CE;color:white;border:1px solid #6755CE}.quiet-button{background:#FCFBF8;color:#51465F;border:1px solid #DED7E7}.primary-button:hover{background:#5543B8}.quiet-button:hover{background:#F0EDF9}button:disabled{opacity:.55;cursor:default}.research-controls[hidden],.research-jobs[hidden],.wallet-actions[hidden]{display:none}.research-jobs{background:#fff;border:1px solid #E6DFEB;border-radius:18px;padding:20px 23px;margin-bottom:26px}.research-jobs .section-heading{margin-bottom:7px}.job-row{display:flex;gap:18px;justify-content:space-between;align-items:center;padding:15px 0;border-top:1px solid #EFEAF2}.job-identity{min-width:0;font-size:12px}.job-identity strong{font-weight:600}.job-identity a{margin-left:8px;color:#6755CE;font-size:11px}.job-identity p{margin:7px 0;color:#686174;font-size:12px}.job-identity small{color:#81778D;font-size:10px}.job-state{display:inline-block;font-size:10px;padding:4px 7px;margin-right:9px;border-radius:6px;background:#F0EDF9;color:#6755CE;text-transform:capitalize}.job-state.partial,.job-state.failed,.job-state.interrupted{background:#FAEFE0;color:#775529}.job-state.succeeded{background:#EDF4EF;color:#366047}.job-identity .job-error{color:#8B513D;line-height:1.6}.job-actions{flex-shrink:0}.research-dialog{width:min(490px,calc(100% - 32px));max-height:85dvh;padding:28px;border:1px solid #E2DCE8;border-radius:22px;background:#FCFBF8;color:#292536;box-shadow:0 25px 90px #29253625;overflow:auto}.research-dialog::backdrop{background:#29253666}.dialog-heading{display:flex;align-items:center;gap:12px;margin-bottom:16px}.dialog-heading h2{font-size:19px;line-height:1.35;margin:0;flex:1}.dialog-heading button{border:0;background:none;color:#686174;font-size:23px;min-width:36px;min-height:36px;cursor:pointer}.research-dialog p{font-size:13px;line-height:1.7;color:#686174}.research-dialog label{display:block;font-size:12px;font-weight:600;margin:18px 0 10px}.research-dialog input:not([type=checkbox]),.research-dialog textarea,.research-dialog select{display:block;margin-top:9px;padding:12px;border:1px solid #DAD3E3;border-radius:9px;background:white;color:#292536;width:100%;font:inherit;line-height:1.5}.research-dialog textarea{resize:vertical}.research-dialog .form-note{font-size:11px}.research-dialog .form-error{color:#9B4437;min-height:0}.research-dialog .form-error:empty{display:none}.research-dialog form+form,.assistant-gate{border-top:1px solid #E2DCE8;margin-top:23px;padding-top:17px}.research-dialog .checkbox-label{display:flex;gap:10px;align-items:center;font-weight:400}.checkbox-label input{width:17px;height:17px;margin:0}.assistant-gate strong{font-size:12px}.assistant-gate p{font-size:11px}.history-dialog{width:min(720px,calc(100% - 32px))}.history-row{display:grid;gap:7px;padding:15px 0;border-top:1px solid #E2DCE8;font-size:12px}.history-row small{word-break:break-all;color:#81778D}.comparison-result{margin-top:24px}.comparison-result table{width:100%;font-size:12px;text-align:left;border-collapse:collapse}.comparison-result td,.comparison-result th{padding:10px 12px;border-bottom:1px solid #E2DCE8}.comparison-result small{display:block;color:#81778D;font-size:10px;margin-top:5px}.comparison-result td:not(:first-child){font-variant-numeric:tabular-nums}.empty .primary-button{margin-top:15px}.research-dialog :focus-visible,.research-controls :focus-visible,.wallet-actions :focus-visible,.job-actions :focus-visible{outline:3px solid #AFA1E7;outline-offset:3px}
|
|
50
50
|
@media(max-width:700px){.brand-logo{width:36px;height:36px}.nav-logo{width:23px;height:23px}.research-controls{align-items:flex-start;flex-direction:column;gap:13px}.research-controls .eyebrow{font-size:9px}.control-buttons{width:100%}.control-buttons button{flex:1}.wallet-actions{justify-content:flex-start}.wallet-actions button{flex:1;white-space:nowrap}.research-jobs{padding:17px}.job-row{align-items:flex-start;gap:8px}.job-identity a{display:block;margin:7px 0}.job-actions .quiet-button{font-size:10px;padding:9px}.research-dialog{padding:21px}.dialog-heading h2{font-size:17px}.note-logo{width:34px;height:34px}.research-controls p{line-height:1.6}.comparison-result .table-wrap{overflow-x:auto}.comparison-result table{min-width:540px}}
|
|
51
51
|
.job-stages{margin:11px 0;font-size:11px;color:#686174}.job-stages summary{cursor:pointer;color:#6755CE}.job-stages ol{padding-left:18px}.job-stages li{padding:5px 0}.job-stages li span{display:block;font-size:10px;line-height:1.7;word-break:break-word}
|
|
52
|
+
|
|
53
|
+
.discovery-notice{margin:22px 0;padding:20px 22px;border:1px solid #e8d8b4;border-radius:12px;background:#fffaf0;color:#534426;line-height:1.6}.discovery-notice>strong{display:block;font-size:14px;font-weight:600}.discovery-notice p{margin:7px 0 0;font-size:13px}.discovery-actions{display:flex;gap:10px;margin-top:14px}.discovery-notice>.quiet-button{margin-top:12px}.discovery-notice details{margin-top:13px;font-size:12px}.discovery-notice summary{cursor:pointer;font-weight:500}.discovery-notice details p{white-space:pre-line;overflow-wrap:anywhere;font-size:12px}.discovery-notice .quiet-button{background:#fff;border-color:#e8d8b4}.discovery-notice [hidden]{display:none!important}@media(max-width:620px){.discovery-notice{padding:16px}.discovery-actions{flex-wrap:wrap}}
|
|
@@ -125,7 +125,11 @@ function renderWatching() {
|
|
|
125
125
|
$('wallet-existing').hidden = !valid || !existing;
|
|
126
126
|
$('wallet-existing-link').href = '#/wallet/'+address.toLowerCase();
|
|
127
127
|
$('wallet-register').disabled = !!submission || !permitted || !valid || !!existing;
|
|
128
|
-
|
|
128
|
+
const discovery=typeof setupReadiness!=='undefined'?setupReadiness:null,ready=discovery?.discovery_key_available===true;
|
|
129
|
+
$('wallet-discovery-warning').hidden=ready||!!submission||state?.demo===true;
|
|
130
|
+
$('wallet-discovery-warning-title').textContent=!discovery?'Token discovery is not verified':discovery.discovery_configured?'Token discovery needs a connection':'Token discovery is off';
|
|
131
|
+
$('wallet-discovery-warning-text').textContent=discovery?.discovery_configured?'The selected indexer credential is unavailable. Connect it before researching, or continue with native and Mint Club assets only.':'Other ERC20 holdings may be missing. Connect an indexer before researching, or continue with native and Mint Club assets only.';
|
|
132
|
+
$('wallet-register').textContent = watchingSubmitting ? 'Saving wallet…' : ready||state?.demo?'Add and research':'Add with limited discovery';
|
|
129
133
|
if (browser && $('wallet-dialog').open && watchingLastStatus === 'requesting' && current.status === 'accounts')
|
|
130
134
|
$('wallet-accounts').querySelector('button')?.focus();
|
|
131
135
|
watchingLastStatus = current.status;
|
|
@@ -17,6 +17,27 @@
|
|
|
17
17
|
return { rows: rows.slice((page - 1) * pageSize, page * pageSize), count: rows.length, page, pages };
|
|
18
18
|
}
|
|
19
19
|
const active = job => ['queued', 'running'].includes(job.state);
|
|
20
|
+
const rpcGap = chain => chain.rpc_available===false||chain.registry_complete===false||chain.candidate_balance_errors>0;
|
|
21
|
+
const coverageGap = chain => !chain.complete||rpcGap(chain);
|
|
22
|
+
function discoveryNotice(wallet, readiness = null) {
|
|
23
|
+
const chains=(wallet?.chains||[]).filter(c=>c.environment==='mainnet');
|
|
24
|
+
const gaps=chains.filter(coverageGap);
|
|
25
|
+
if(!wallet?.analysed_at||!gaps.length)return null;
|
|
26
|
+
const missing=chains.some(c=>!c.complete);
|
|
27
|
+
const disabled=(readiness?.discovery_provider==='none'&&chains.every(c=>!c.complete))||(!readiness&&chains.every(c=>c.discovery_status==='disabled'));
|
|
28
|
+
const noKey=readiness?.discovery_configured&&!readiness.discovery_key_available;
|
|
29
|
+
const rpc=chains.filter(rpcGap);
|
|
30
|
+
if(rpc.length){
|
|
31
|
+
const connection=missing&&(disabled||noKey);
|
|
32
|
+
return {title:'Some balances could not be checked',
|
|
33
|
+
message:'On-chain checks did not finish on '+rpc.length+(rpc.length===1?' network.':' networks.')+' Missing balances are unknown, so this portfolio is partial. Wait a moment, then retry holdings. If errors continue, review RPC settings or use a different provider.'+(connection?' Token discovery also needs a connection to find other ERC20 holdings.':''),
|
|
34
|
+
action:'refresh',settings:true,rpcIssues:true,refreshLabel:'Retry holdings',settingsLabel:connection?'Review connections':'Review RPC settings'};
|
|
35
|
+
}
|
|
36
|
+
if(missing&&disabled)return {title:'Token discovery is off',message:'This analysis checks native balances and Mint Club assets only. Other ERC20 holdings may be missing. Connect token discovery, then refresh holdings.',action:'settings'};
|
|
37
|
+
if(missing&&noKey)return {title:'Token discovery needs a connection',message:'The selected indexer credential is unavailable. Review its connection, then refresh holdings. This portfolio is partial.',action:'settings'};
|
|
38
|
+
if(missing&&readiness?.discovery_key_available&&chains.some(c=>['disabled','missing_credential'].includes(c.discovery_status)))return {title:'Refresh holdings to discover other tokens',message:'Your discovery connection is now available. This saved analysis predates it and remains partial until you refresh holdings.',action:'refresh'};
|
|
39
|
+
return {title:'Your portfolio is partial',message:'Some token discovery or on-chain checks did not complete. Missing holdings remain unknown. Review the network details below before refreshing holdings.',action:readiness?.discovery_key_available?'refresh':'settings'};
|
|
40
|
+
}
|
|
20
41
|
function jobPresentation(job, now = Date.now(), connected = true) {
|
|
21
42
|
const labels = { queued: 'Queued', running: 'Running', succeeded: 'Completed', partial: 'Saved with gaps', interrupted: 'Interrupted', failed: 'Failed', cancelled: 'Stopped' };
|
|
22
43
|
const start = job.state === 'queued' ? job.updated_at || job.created_at : job.started_at || job.created_at;
|
|
@@ -37,7 +58,7 @@
|
|
|
37
58
|
const h = Math.floor(seconds / 3600), m = Math.floor(seconds % 3600 / 60), s = seconds % 60;
|
|
38
59
|
return (h ? h + 'h ' : '') + m + 'm ' + s + 's';
|
|
39
60
|
}
|
|
40
|
-
const api = { catalog, pageSize, active, jobPresentation, duration };
|
|
61
|
+
const api = { catalog, pageSize, active, jobPresentation, duration, discoveryNotice, coverageGap };
|
|
41
62
|
if (typeof module !== 'undefined' && module.exports) module.exports = api;
|
|
42
63
|
else root.KiraView = api;
|
|
43
64
|
})(typeof globalThis !== 'undefined' ? globalThis : this);
|
|
@@ -273,6 +273,13 @@ main { margin-left: 232px; width: calc(100% - 232px); }
|
|
|
273
273
|
.research-dialog p, .research-dialog label { font-size: 14px; line-height: 1.6; }
|
|
274
274
|
.research-dialog .form-note, .assistant-gate p { font-size: 12px; line-height: 1.6; }
|
|
275
275
|
.research-dialog input:not([type=checkbox]), .research-dialog textarea, .research-dialog select { font-size: 14px; min-height: 44px; }
|
|
276
|
+
.ows-provider-card { padding: 17px 18px; margin: 0 0 16px; border: 1px solid #e2d8e9; border-radius: 14px; background: #f2edf6; }
|
|
277
|
+
.ows-provider-heading { display: flex; align-items: center; flex-wrap: wrap; gap: 12px; margin-bottom: 8px; color: var(--secondary); font-size: 12px; }
|
|
278
|
+
.ows-provider-heading a { display: inline-flex; align-items: center; gap: 10px; min-height: 44px; color: var(--secondary); }
|
|
279
|
+
.ows-provider-heading img { display: block; width: 148px; height: 31px; object-fit: contain; filter: brightness(0); opacity: .8; }
|
|
280
|
+
.ows-provider-heading a:hover img { opacity: 1; }
|
|
281
|
+
.ows-provider-card h3 { margin: 0 0 8px; font-size: 15px; line-height: 1.4; font-weight: 600; color: var(--ink); }
|
|
282
|
+
.research-dialog .ows-provider-card p { margin: 0; font-size: 13px; line-height: 1.6; }
|
|
276
283
|
.history-row { font-size: 14px; line-height: 1.6; }
|
|
277
284
|
.history-row small, .comparison-result small { font-size: 12px; }
|
|
278
285
|
|
package/wallet.py
CHANGED
|
@@ -41,7 +41,9 @@ def atomic(path, data):
|
|
|
41
41
|
temp = path.with_suffix(path.suffix+'.tmp')
|
|
42
42
|
text = data if isinstance(data, str) else json.dumps(data, indent=2, ensure_ascii=False)+'\n'
|
|
43
43
|
# Providers occasionally include credentialed URLs in an error response.
|
|
44
|
-
|
|
44
|
+
try:values=h.secrets().values()
|
|
45
|
+
except ValueError:values=[]
|
|
46
|
+
for secret in values:
|
|
45
47
|
if secret:
|
|
46
48
|
text=text.replace(secret,'[redacted]')
|
|
47
49
|
temp.write_text(text)
|
|
@@ -92,9 +94,10 @@ def discover(wallet, n, folder):
|
|
|
92
94
|
cached=read(evidence)
|
|
93
95
|
if cached['wallet'].lower()==wallet.lower() and cached['complete']:
|
|
94
96
|
return cached
|
|
95
|
-
row = {'wallet':wallet,'chain_id':cid,'observed_at':h.now(),'complete':False,'tokens':[],'native':[], 'pages':[],'source':None}
|
|
97
|
+
row = {'wallet':wallet,'chain_id':cid,'observed_at':h.now(),'complete':False,'tokens':[],'native':[], 'pages':[],'source':None,'discovery_status':'incomplete'}
|
|
96
98
|
cfg=load_config()
|
|
97
|
-
key=h.secrets().get('ALCHEMY_CUSTOM_APY_KEY') if cfg['discovery']['provider']=='alchemy' else None
|
|
99
|
+
try:key=h.secrets().get('ALCHEMY_CUSTOM_APY_KEY') if cfg['discovery']['provider']=='alchemy' else None
|
|
100
|
+
except ValueError:key=None
|
|
98
101
|
if cid in ALCHEMY and key:
|
|
99
102
|
row['source']='Alchemy Portfolio Tokens By Wallet'
|
|
100
103
|
network=ALCHEMY[cid]
|
|
@@ -147,7 +150,14 @@ def discover(wallet, n, folder):
|
|
|
147
150
|
else:
|
|
148
151
|
row['error']=response
|
|
149
152
|
else:
|
|
150
|
-
|
|
153
|
+
status='disabled' if cfg['discovery']['provider']=='none' else 'missing_credential' if cid in ALCHEMY and not key else 'unsupported'
|
|
154
|
+
row['discovery_status']=status
|
|
155
|
+
messages={'disabled':'Token discovery is off. Configure an indexer to discover other ERC20 holdings.',
|
|
156
|
+
'missing_credential':'The selected indexer credential is unavailable. Configure its private environment reference, then refresh holdings.',
|
|
157
|
+
'unsupported':'No general ERC20 indexer is available for this network.'}
|
|
158
|
+
row['error']={'message':messages[status]+' Mint Club registry and native balances are queried independently.'}
|
|
159
|
+
if row['complete']:row['discovery_status']='checked'
|
|
160
|
+
elif row['source']:row['discovery_status']='provider_error'
|
|
151
161
|
row['tokens']=list({t['address'].lower():t for t in row['tokens']}.values())
|
|
152
162
|
atomic(evidence,row)
|
|
153
163
|
return row
|
|
@@ -228,6 +238,8 @@ def finish(wallet, folder, networks, discovered):
|
|
|
228
238
|
'rpc_endpoint_indices':c.get('rpc_endpoint_indices',[]),
|
|
229
239
|
'general_erc20_discovery':'indexer_checked' if d['complete'] else 'incomplete',
|
|
230
240
|
'indexer_source':d['source'],'indexer_pages':len(d['pages']),'indexer_complete':d['complete'],
|
|
241
|
+
'discovery_status':d.get('discovery_status','checked' if d['complete'] else 'incomplete'),
|
|
242
|
+
'candidate_balance_errors':len(c.get('balance_errors',[])),
|
|
231
243
|
'positive_erc20_count_discovered':sum(t['chain_id']==n['chain_id'] and t['token_type']=='ERC20' for t in tokens),
|
|
232
244
|
'mintclub_inventory_scan':None,'mintclub_registry_scan':c.get('registry_scan'),'notes':[]}
|
|
233
245
|
if not d['complete']:row['notes'].append('General token discovery incomplete. See discovery evidence; this is not proof of no holdings.')
|
|
@@ -264,7 +276,7 @@ def finish(wallet, folder, networks, discovered):
|
|
|
264
276
|
'dex_tokens_with_liquidity_evidence':sum(t['dex_liquidity_found'] for t in tokens),'mintclub_tokens':len(minted),
|
|
265
277
|
'mintclub_tokens_with_nonzero_reserve':sum(t['mintclub']['funded'] for t in minted),
|
|
266
278
|
'mintclub_registry_assets_checked':sum(c.get('mintclub_registry_scan',{}).get('checked',0) if c.get('mintclub_registry_scan') else 0 for c in coverage)}
|
|
267
|
-
gaps=any(not c['indexer_complete'] or not (c.get('mintclub_registry_scan') or {}).get('complete') or c['rpc_status']!='available' for c in coverage)
|
|
279
|
+
gaps=any(not c['indexer_complete'] or not (c.get('mintclub_registry_scan') or {}).get('complete') or c['rpc_status']!='available' or c['candidate_balance_errors'] for c in coverage)
|
|
268
280
|
result={'schema_version':1,'wallet_address':wallet['address'],'tags':wallet['tags'],'compiled_at':h.now(),'status':'completed_with_coverage_gaps' if gaps else 'completed',
|
|
269
281
|
'scope':'Direct ERC20, registered Mint Club ERC1155 and native holdings across all configured Mint Club EVM networks. Testnets are separate.',
|
|
270
282
|
'counts':counts,'coverage':coverage,'tokens':tokens,'nested_curve_independent_redemption_estimates':[], 'price_references':refs,
|