txray 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/CONTRIBUTING.md +21 -0
- package/LICENSE +21 -0
- package/README.md +54 -2
- package/SECURITY.md +9 -0
- package/dist/adapters/examples.js +173 -0
- package/dist/adapters/rpc.js +132 -0
- package/dist/app.js +111 -0
- package/dist/cli.js +38 -0
- package/dist/domain/analyze.js +161 -0
- package/dist/domain/base58.js +48 -0
- package/dist/domain/errors.js +192 -0
- package/dist/domain/instructions.js +54 -0
- package/dist/domain/logs.js +47 -0
- package/dist/domain/schema.js +90 -0
- package/dist/public/app.js +7 -0
- package/dist/public/architecture.html +79 -0
- package/dist/public/architecture.svg +17 -0
- package/dist/public/chevron.svg +1 -0
- package/dist/public/guide.html +66 -0
- package/dist/public/index.html +180 -0
- package/dist/public/mark.svg +1 -0
- package/dist/public/styles.css +865 -0
- package/dist/server.js +26 -0
- package/docs/api.md +37 -0
- package/docs/architecture.md +32 -0
- package/docs/architecture.svg +17 -0
- package/docs/guide.md +41 -0
- package/docs/screenshot.png +0 -0
- package/package.json +80 -4
package/dist/server.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { createApp } from "./app.js";
|
|
3
|
+
try {
|
|
4
|
+
const port = z.coerce
|
|
5
|
+
.number()
|
|
6
|
+
.int()
|
|
7
|
+
.min(1024)
|
|
8
|
+
.max(65535)
|
|
9
|
+
.parse(process.env.PORT ?? 4450);
|
|
10
|
+
const server = createApp().listen(port, "127.0.0.1", () => {
|
|
11
|
+
console.log(`txray: http://127.0.0.1:${port}`);
|
|
12
|
+
console.log("Read-only. No wallet, keys or funds required.");
|
|
13
|
+
});
|
|
14
|
+
server.requestTimeout = 16000;
|
|
15
|
+
server.headersTimeout = 10000;
|
|
16
|
+
server.on("error", (error) => {
|
|
17
|
+
console.error(error.code === "EADDRINUSE"
|
|
18
|
+
? `Port ${port} is already in use. Set another PORT.`
|
|
19
|
+
: "Could not start txray.");
|
|
20
|
+
process.exitCode = 1;
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
console.error("PORT must be an integer between 1024 and 65535.");
|
|
25
|
+
process.exitCode = 1;
|
|
26
|
+
}
|
package/docs/api.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Local API
|
|
2
|
+
|
|
3
|
+
Base address: `http://127.0.0.1:4450`. JSON responses are not cached. There is no authentication; the server binds to loopback and rejects foreign Host and Origin headers. This is a local tool, not a hosted service.
|
|
4
|
+
|
|
5
|
+
## Inspect a live transaction
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
curl http://127.0.0.1:4450/api/transactions \
|
|
9
|
+
-H 'Content-Type: application/json' \
|
|
10
|
+
-d '{"signature":"YOUR_TRANSACTION_SIGNATURE","network":"devnet"}'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
`signature` must decode to 64 bytes of base58. `network` is `devnet` or `mainnet-beta`. Extra fields, arbitrary URLs and oversized request bodies are rejected. The placeholder above must be replaced before running.
|
|
14
|
+
|
|
15
|
+
The server calls `getTransaction` with `encoding: "json"`, `commitment: "confirmed"` and `maxSupportedTransactionVersion: 1`.
|
|
16
|
+
|
|
17
|
+
A report contains `schemaVersion`, `signature`, `network`, `source`, `checkedAt`, `slot`, `version`, `status`, `diagnosis`, `fee`, `compute`, `instructions`, `logs`, `warnings`, `summaryText` and `raw`. Unavailable values are `null` or carry an explicit unknown state. Monetary values and large integers use exact decimal strings. `raw` is the validated RPC record, with known integer fields normalized to strings and unknown fields retained.
|
|
18
|
+
|
|
19
|
+
Instruction `index` is zero-based; `number` is one-based. The identified failed program is the top-level program. Inner calls and logs are preserved without claiming a verified innermost cause. Requested settings do not establish the exact charged base/priority fee split.
|
|
20
|
+
|
|
21
|
+
## Offline examples
|
|
22
|
+
|
|
23
|
+
- `GET /api/examples`: example names and descriptions.
|
|
24
|
+
- `GET /api/examples/insufficient-funds`: a complete fictional report.
|
|
25
|
+
- Other IDs: `unknown-program`, `successful-v0`, `missing-metadata`, `v1-config`.
|
|
26
|
+
|
|
27
|
+
Examples run the same analyzer without querying RPC. Every example report has `source: "example"`.
|
|
28
|
+
|
|
29
|
+
`GET /api/health` reports that the local HTTP server is running; it does not assert RPC availability.
|
|
30
|
+
|
|
31
|
+
## Errors and bounds
|
|
32
|
+
|
|
33
|
+
Errors use `{ "error": { "code": "TRANSACTION_NOT_FOUND", "message": "..." } }` with a non-200 status. Not found is 404; invalid input is 400; rejected origin is 403; non-JSON body is 415; excessive request size is 413; throttling is 429; RPC problems use 502, 503 or 504. Throttled responses include `Retry-After` when available.
|
|
34
|
+
|
|
35
|
+
The server accepts 16 KiB JSON requests and 2 MiB RPC responses, with a 12-second RPC timeout, four concurrent lookups and 30 live lookups per minute. No automatic retries. A null RPC result may mean wrong network, unconfirmed, pruned or unavailable history; it does not prove nonexistence.
|
|
36
|
+
|
|
37
|
+
Browser export is a local download of this report, not another API call.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
The browser asks a local Express server for a transaction. The server queries a fixed public Solana RPC endpoint, validates the response, and runs a pure analyzer. No database, wallet, hosted backend or paid API is required.
|
|
6
|
+
|
|
7
|
+
| Part | Responsibility |
|
|
8
|
+
| -------------------------- | --------------------------------------------------------------------------------------- |
|
|
9
|
+
| `src/cli.ts` | npm executable, help, version and local port options |
|
|
10
|
+
| `src/web/app.ts` | Signature form, diagnostics, timeline, text copy and JSON download |
|
|
11
|
+
| `src/app.ts` | Local HTTP boundary, input checks, security headers and lookup limits |
|
|
12
|
+
| `src/adapters/rpc.ts` | Confirmed JSON RPC reads, exact integers, timeout and response cap |
|
|
13
|
+
| `src/domain/` | Response schema, account resolution, known errors, instruction decoding and log parsing |
|
|
14
|
+
| `src/adapters/examples.ts` | Clearly labeled fictional records, analyzed by the same domain code |
|
|
15
|
+
|
|
16
|
+
## Trust boundaries
|
|
17
|
+
|
|
18
|
+
Only the fixed Mainnet and Devnet endpoints can be queried. User-supplied RPC URLs are not accepted. The server binds to `127.0.0.1` and checks Host, Origin and cross-site fetch headers. The browser uses a same-origin content security policy. Log text is inserted as text, never HTML.
|
|
19
|
+
|
|
20
|
+
The RPC response is the source of observations. Known runtime errors are mapped conservatively; custom errors have no guessed meanings. Fee amounts and large integers remain exact decimal strings. Per-program logs do not override `meta.err` or establish a root cause by themselves.
|
|
21
|
+
|
|
22
|
+
Live results stay in process memory for the request and in the current browser view. They are not written to disk. Examples contain no real transaction claims. The only file output is a user-requested browser export.
|
|
23
|
+
|
|
24
|
+
## Running and testing
|
|
25
|
+
|
|
26
|
+
`npm run demo` builds the TypeScript server, bundles the small browser entry and generates the guide and architecture pages. Both pages are available inside the dashboard without a Markdown viewer or internet.
|
|
27
|
+
|
|
28
|
+
`npx txray` starts the prebuilt server and browser assets from the installed package. Consumers do not need TypeScript or build tools. The npm archive includes only built runtime files, public documentation and the license.
|
|
29
|
+
|
|
30
|
+
Unit tests cover domain behavior. API tests replace the external fetch boundary, while running real handlers. Browser tests use a local test server and exercise the full form, rendering and export path without a public RPC dependency.
|
|
31
|
+
|
|
32
|
+
[Guide](guide.md)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="960" height="530" viewBox="0 0 960 530" role="img" aria-labelledby="title desc">
|
|
2
|
+
<title id="title">txray architecture</title><desc id="desc">A local browser sends a transaction signature to a local Express server. The server reads a fixed Solana RPC endpoint, validates the response, and analyzes errors and logs. Results return to the dashboard for copy or JSON export. Offline examples bypass RPC. No data is stored.</desc>
|
|
3
|
+
<rect width="960" height="530" rx="12" fill="#10171f"/>
|
|
4
|
+
<g font-family="Arial,sans-serif" fill="#e3eaf0">
|
|
5
|
+
<text x="36" y="49" font-size="26" font-weight="700">From signature to diagnosis</text>
|
|
6
|
+
<text x="36" y="80" font-size="17" fill="#a2b1bf">Read-only. Local processing. No saved lookups.</text>
|
|
7
|
+
<g fill="#18232d" stroke="#334553"><rect x="36" y="124" width="256" height="112" rx="9"/><rect x="352" y="124" width="256" height="112" rx="9"/><rect x="668" y="124" width="256" height="112" rx="9"/><rect x="352" y="307" width="256" height="112" rx="9"/><rect x="36" y="307" width="256" height="112" rx="9"/><rect x="668" y="307" width="256" height="112" rx="9"/></g>
|
|
8
|
+
<text x="56" y="162" font-size="22" font-weight="700">Local dashboard</text><text x="56" y="191" font-size="16" fill="#a2b1bf">Signature + network</text><text x="56" y="216" font-size="16" fill="#a2b1bf">127.0.0.1:4450</text>
|
|
9
|
+
<text x="372" y="162" font-size="22" font-weight="700">Express server</text><text x="372" y="191" font-size="16" fill="#a2b1bf">Validate input and response</text><text x="372" y="216" font-size="16" fill="#a2b1bf">Timeout, size and lookup caps</text>
|
|
10
|
+
<text x="688" y="162" font-size="22" font-weight="700">Solana public RPC</text><text x="688" y="191" font-size="16" fill="#a2b1bf">Mainnet / Devnet</text><text x="688" y="216" font-size="16" fill="#a2b1bf">getTransaction (confirmed)</text>
|
|
11
|
+
<text x="372" y="347" font-size="22" font-weight="700">Pure analyzer</text><text x="372" y="377" font-size="16" fill="#a2b1bf">Errors, instructions, logs</text><text x="372" y="402" font-size="16" fill="#a2b1bf">Exact fees and compute</text>
|
|
12
|
+
<text x="56" y="347" font-size="22" font-weight="700">Readable report</text><text x="56" y="377" font-size="16" fill="#a2b1bf">Timeline and diagnostics</text><text x="56" y="402" font-size="16" fill="#a2b1bf">Copy text / export JSON</text>
|
|
13
|
+
<text x="688" y="347" font-size="22" font-weight="700">Offline examples</text><text x="688" y="377" font-size="16" fill="#a2b1bf">Fictional records</text><text x="688" y="402" font-size="16" fill="#a2b1bf">Same analyzer, no RPC</text>
|
|
14
|
+
<g stroke="#90c9ee" stroke-width="2" fill="none"><path d="M295 178h50m-9-7 9 7-9 7M611 166h50m-9-7 9 7-9 7M661 198h-50m9-7-9 7 9 7M480 242v57m-7-9 7 9 7-9M345 363h-45m9-7-9 7 9 7M661 363h-45m9-7-9 7 9 7"/></g>
|
|
15
|
+
<text x="36" y="475" font-size="17" fill="#a2b1bf">No wallet, signing, database, cloud deployment or paid API.</text>
|
|
16
|
+
<text x="36" y="502" font-size="17" fill="#a2b1bf">Unknown errors and missing metadata remain visible.</text>
|
|
17
|
+
</g></svg>
|
package/docs/guide.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Using txray
|
|
2
|
+
|
|
3
|
+
Run `npx txray` with Node.js 22.18+. Open the printed local URL. Use `npx txray --port 4452` if the default port is busy. Press Ctrl+C to stop.
|
|
4
|
+
|
|
5
|
+
Paste a Solana transaction signature, select its network and choose **Inspect transaction**. A transaction signature is the public identifier of a transaction, not a wallet address.
|
|
6
|
+
|
|
7
|
+
Start with an **offline example** if you do not have a signature. All example values are fictional. No RPC request is made for an example.
|
|
8
|
+
|
|
9
|
+
## Read a result
|
|
10
|
+
|
|
11
|
+
1. **Diagnosis:** a known runtime error gets a plain explanation. Custom program errors remain unknown; a code such as `6001` means different things in different programs.
|
|
12
|
+
2. **Failed instruction:** `#2` means RPC index `1`. This identifies the top-level instruction, not necessarily the innermost cause of a nested failure.
|
|
13
|
+
3. **Fee and compute:** the fee is the total recorded in metadata. Compute consumption comes from `meta.computeUnitsConsumed`. Missing values say **Not reported**, never zero.
|
|
14
|
+
4. **Instruction timeline:** earlier instructions ran before the failure; later instructions did not run. Ordinary program state changes roll back. Fees and durable-nonce advancement can persist.
|
|
15
|
+
5. **Execution log:** lines appear in recorded order. Indentation follows runtime invocation depth. A log's wording is evidence of what a program printed, not an independently verified explanation.
|
|
16
|
+
|
|
17
|
+
An inner call can fail and be caught by its caller; only `meta.err` determines the transaction outcome. Nested calls can report overlapping compute consumption. txray does not add those lines together. Scheduling cost units are a separate metric from compute units.
|
|
18
|
+
|
|
19
|
+
## Save or share
|
|
20
|
+
|
|
21
|
+
**Copy report** copies a text diagnosis. **Export JSON** saves the diagnosis and the full returned transaction record, including logs and instructions. Large integer values are decimal strings so exports do not round balances or fees.
|
|
22
|
+
|
|
23
|
+
Live lookup signatures are sent to the selected official public RPC. txray stores no lookup history and makes no wallet requests. Exports are saved only when you request one.
|
|
24
|
+
|
|
25
|
+
## Limits
|
|
26
|
+
|
|
27
|
+
- Reads confirmed Mainnet or Devnet records. A confirmed record can still be affected by a fork; confirmed is not finalized.
|
|
28
|
+
- Legacy, v0 and v1 are requested. Newer versions and unsupported shapes are reported explicitly.
|
|
29
|
+
- v0 addresses resolve in static, loaded writable, loaded readonly order. Missing addresses remain unresolved.
|
|
30
|
+
- v1 compute and absolute priority fees come from the message config. ComputeBudget instructions are ignored for v1 configuration.
|
|
31
|
+
- The RPC supplies a total fee, not an exact base/priority split. Requested compute settings are shown separately, not presented as charged fees.
|
|
32
|
+
- Only basic System transfer and Compute Budget instructions are decoded. Unknown instruction bytes remain available. No token transfer, price, profit or full balance-change interpretation is inferred.
|
|
33
|
+
- Missing metadata, missing logs and runtime log truncation stay visible. txray cannot reconstruct omitted execution.
|
|
34
|
+
- Public RPC can rate limit, block access or lack historical records. A not-found result does not prove a transaction never existed. Check network and retry later.
|
|
35
|
+
- No automatic retries. Live calls time out after 12 seconds, responses are capped at 2 MiB, and the local server permits four concurrent lookups and 30 live lookups per minute.
|
|
36
|
+
|
|
37
|
+
[Architecture](architecture.md)
|
|
38
|
+
|
|
39
|
+
## Official references
|
|
40
|
+
|
|
41
|
+
[Solana getTransaction](https://solana.com/docs/rpc/http/gettransaction), [versioned transactions](https://solana.com/docs/core/transactions/versioned-transactions), [fees](https://solana.com/docs/core/fees), and [RPC networks](https://solana.com/docs/references/clusters).
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,82 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "txray",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"description": "A local-first Solana transaction debugger. Follow execution, explain failures and export diagnostics.",
|
|
7
|
+
"author": "Royal Pinto <royalpinto007@gmail.com>",
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=22.18.0"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"demo": "npm run build && node dist/server.js",
|
|
13
|
+
"start": "node dist/server.js",
|
|
14
|
+
"dev": "npm run build && tsx watch src/server.ts",
|
|
15
|
+
"build": "tsc -p tsconfig.build.json && node scripts/build.mjs",
|
|
16
|
+
"format": "prettier --write .",
|
|
17
|
+
"format:check": "prettier --check .",
|
|
18
|
+
"lint": "eslint .",
|
|
19
|
+
"typecheck": "tsc --noEmit",
|
|
20
|
+
"test": "tsx --test tests/*.test.ts",
|
|
21
|
+
"test:e2e": "npm run build && playwright test",
|
|
22
|
+
"check": "npm run format:check && npm run lint && npm run typecheck && npm test && npm run build",
|
|
23
|
+
"prepack": "npm run build",
|
|
24
|
+
"package:check": "npm run build && node scripts/check-package.mjs"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"express": "^5.3.0",
|
|
28
|
+
"helmet": "^8.3.0",
|
|
29
|
+
"zod": "^4.6.7"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@eslint/js": "^10.0.1",
|
|
33
|
+
"@playwright/test": "^1.64.1",
|
|
34
|
+
"@types/express": "^5.0.6",
|
|
35
|
+
"@types/node": "^22.20.5",
|
|
36
|
+
"@types/supertest": "^7.2.1",
|
|
37
|
+
"esbuild": "^0.28.2",
|
|
38
|
+
"eslint": "^10.13.0",
|
|
39
|
+
"marked": "^18.1.1",
|
|
40
|
+
"prettier": "^3.9.10",
|
|
41
|
+
"supertest": "^7.3.1",
|
|
42
|
+
"tsx": "^4.23.15",
|
|
43
|
+
"typescript": "^6.0.3",
|
|
44
|
+
"typescript-eslint": "^8.72.0"
|
|
45
|
+
},
|
|
46
|
+
"bin": {
|
|
47
|
+
"txray": "dist/cli.js"
|
|
48
|
+
},
|
|
49
|
+
"files": [
|
|
50
|
+
"dist/",
|
|
51
|
+
"docs/*.md",
|
|
52
|
+
"docs/*.svg",
|
|
53
|
+
"docs/screenshot.png",
|
|
54
|
+
"CONTRIBUTING.md",
|
|
55
|
+
"CHANGELOG.md",
|
|
56
|
+
"SECURITY.md"
|
|
57
|
+
],
|
|
58
|
+
"repository": {
|
|
59
|
+
"type": "git",
|
|
60
|
+
"url": "git+https://github.com/royalpinto007/txray.git"
|
|
61
|
+
},
|
|
62
|
+
"homepage": "https://www.npmjs.com/package/txray",
|
|
63
|
+
"bugs": {
|
|
64
|
+
"url": "https://github.com/royalpinto007/txray/issues"
|
|
65
|
+
},
|
|
66
|
+
"keywords": [
|
|
67
|
+
"solana",
|
|
68
|
+
"transaction",
|
|
69
|
+
"debugger",
|
|
70
|
+
"rpc",
|
|
71
|
+
"logs",
|
|
72
|
+
"compute-units",
|
|
73
|
+
"developer-tools",
|
|
74
|
+
"cli",
|
|
75
|
+
"local-first",
|
|
76
|
+
"typescript"
|
|
77
|
+
],
|
|
78
|
+
"publishConfig": {
|
|
79
|
+
"access": "public",
|
|
80
|
+
"registry": "https://registry.npmjs.org/"
|
|
81
|
+
}
|
|
82
|
+
}
|