chia-explorer 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/LICENSE +21 -0
- package/README.md +81 -0
- package/dist/chia/amounts.d.ts +11 -0
- package/dist/chia/amounts.js +52 -0
- package/dist/chia/amounts.js.map +1 -0
- package/dist/chia/bech32.d.ts +8 -0
- package/dist/chia/bech32.js +31 -0
- package/dist/chia/bech32.js.map +1 -0
- package/dist/chia/coin-name.d.ts +15 -0
- package/dist/chia/coin-name.js +40 -0
- package/dist/chia/coin-name.js.map +1 -0
- package/dist/chia/hex.d.ts +4 -0
- package/dist/chia/hex.js +26 -0
- package/dist/chia/hex.js.map +1 -0
- package/dist/coinset/agent.d.ts +7 -0
- package/dist/coinset/agent.js +26 -0
- package/dist/coinset/agent.js.map +1 -0
- package/dist/coinset/pagination.d.ts +24 -0
- package/dist/coinset/pagination.js +42 -0
- package/dist/coinset/pagination.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/network.d.ts +6 -0
- package/dist/network.js +18 -0
- package/dist/network.js.map +1 -0
- package/dist/prompts/address-summary.d.ts +2 -0
- package/dist/prompts/address-summary.js +34 -0
- package/dist/prompts/address-summary.js.map +1 -0
- package/dist/prompts/block-summary.d.ts +2 -0
- package/dist/prompts/block-summary.js +50 -0
- package/dist/prompts/block-summary.js.map +1 -0
- package/dist/prompts/network-status.d.ts +2 -0
- package/dist/prompts/network-status.js +29 -0
- package/dist/prompts/network-status.js.map +1 -0
- package/dist/schemas/common.d.ts +7 -0
- package/dist/schemas/common.js +24 -0
- package/dist/schemas/common.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +42 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/addresses/address-to-puzzle-hash.d.ts +2 -0
- package/dist/tools/addresses/address-to-puzzle-hash.js +21 -0
- package/dist/tools/addresses/address-to-puzzle-hash.js.map +1 -0
- package/dist/tools/addresses/puzzle-hash-to-address.d.ts +2 -0
- package/dist/tools/addresses/puzzle-hash-to-address.js +24 -0
- package/dist/tools/addresses/puzzle-hash-to-address.js.map +1 -0
- package/dist/tools/blockchain/count-block-transactions.d.ts +2 -0
- package/dist/tools/blockchain/count-block-transactions.js +52 -0
- package/dist/tools/blockchain/count-block-transactions.js.map +1 -0
- package/dist/tools/blockchain/get-block-by-hash.d.ts +2 -0
- package/dist/tools/blockchain/get-block-by-hash.js +27 -0
- package/dist/tools/blockchain/get-block-by-hash.js.map +1 -0
- package/dist/tools/blockchain/get-block-by-height.d.ts +2 -0
- package/dist/tools/blockchain/get-block-by-height.js +28 -0
- package/dist/tools/blockchain/get-block-by-height.js.map +1 -0
- package/dist/tools/blockchain/get-blockchain-state.d.ts +2 -0
- package/dist/tools/blockchain/get-blockchain-state.js +37 -0
- package/dist/tools/blockchain/get-blockchain-state.js.map +1 -0
- package/dist/tools/blockchain/get-netspace.d.ts +2 -0
- package/dist/tools/blockchain/get-netspace.js +26 -0
- package/dist/tools/blockchain/get-netspace.js.map +1 -0
- package/dist/tools/blockchain/get-peak-height.d.ts +2 -0
- package/dist/tools/blockchain/get-peak-height.js +23 -0
- package/dist/tools/blockchain/get-peak-height.js.map +1 -0
- package/dist/tools/coins/calculate-coin-name.d.ts +2 -0
- package/dist/tools/coins/calculate-coin-name.js +28 -0
- package/dist/tools/coins/calculate-coin-name.js.map +1 -0
- package/dist/tools/coins/get-balance.d.ts +2 -0
- package/dist/tools/coins/get-balance.js +35 -0
- package/dist/tools/coins/get-balance.js.map +1 -0
- package/dist/tools/coins/get-coin-by-name.d.ts +2 -0
- package/dist/tools/coins/get-coin-by-name.js +34 -0
- package/dist/tools/coins/get-coin-by-name.js.map +1 -0
- package/dist/tools/coins/get-coin-records-by-puzzle-hash.d.ts +2 -0
- package/dist/tools/coins/get-coin-records-by-puzzle-hash.js +36 -0
- package/dist/tools/coins/get-coin-records-by-puzzle-hash.js.map +1 -0
- package/dist/tools/shared/address-input.d.ts +12 -0
- package/dist/tools/shared/address-input.js +26 -0
- package/dist/tools/shared/address-input.js.map +1 -0
- package/dist/tools/shared/response.d.ts +3 -0
- package/dist/tools/shared/response.js +16 -0
- package/dist/tools/shared/response.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +7 -0
- package/dist/version.js.map +1 -0
- package/package.json +75 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Spacetime Technology
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# chia-explorer
|
|
2
|
+
|
|
3
|
+
MCP server that answers questions about the Chia blockchain. Read-only, no keys, no signing.
|
|
4
|
+
|
|
5
|
+
Backed by the public [coinset.org](https://www.coinset.org) full-node API. Supports mainnet (default) and testnet11. Auto-detects network from `xch` or `txch` address prefixes.
|
|
6
|
+
|
|
7
|
+
## What it can answer
|
|
8
|
+
|
|
9
|
+
- What is the current netspace? Peak height? Sync status?
|
|
10
|
+
- What is the header hash of block N? How many transactions in that block?
|
|
11
|
+
- What is the balance of `xch1...` (or `txch1...`, or a raw puzzle hash)?
|
|
12
|
+
- Look up a coin by name. Calculate a coin name from its fields.
|
|
13
|
+
- Convert between addresses and puzzle hashes in either direction.
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install -g chia-explorer
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Or run via `npx`:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npx chia-explorer
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Use with Claude Code
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
claude mcp add chia-explorer -- npx chia-explorer
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Use with Claude Desktop
|
|
34
|
+
|
|
35
|
+
Add to your `claude_desktop_config.json`:
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"mcpServers": {
|
|
40
|
+
"chia-explorer": {
|
|
41
|
+
"command": "npx",
|
|
42
|
+
"args": ["chia-explorer"]
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Tools
|
|
49
|
+
|
|
50
|
+
| Tool | What it does |
|
|
51
|
+
| --- | --- |
|
|
52
|
+
| `get_blockchain_state` | Peak height, netspace (EiB), difficulty, sync status |
|
|
53
|
+
| `get_netspace` | Just the netspace in bytes, EiB, and PiB |
|
|
54
|
+
| `get_peak_height` | Just the peak height |
|
|
55
|
+
| `get_block_by_height` | Block record + header hash for a height |
|
|
56
|
+
| `get_block_by_hash` | Block record for a header hash |
|
|
57
|
+
| `count_block_transactions` | Coin spends, additions, removals counts for a block |
|
|
58
|
+
| `get_balance` | Balance of an address or puzzle hash (paginated, unspent coins) |
|
|
59
|
+
| `get_coin_records_by_puzzle_hash` | Raw coin records for a puzzle hash or address |
|
|
60
|
+
| `get_coin_by_name` | Coin record by coin name |
|
|
61
|
+
| `calculate_coin_name` | sha256(parent_coin_info \|\| puzzle_hash \|\| amount) — no RPC |
|
|
62
|
+
| `address_to_puzzle_hash` | bech32m decode — no RPC |
|
|
63
|
+
| `puzzle_hash_to_address` | bech32m encode — no RPC |
|
|
64
|
+
|
|
65
|
+
All tools take an optional `network: "mainnet" | "testnet11"` (default `mainnet`).
|
|
66
|
+
|
|
67
|
+
## Prompts
|
|
68
|
+
|
|
69
|
+
- `network_status` — quick snapshot of current chain state
|
|
70
|
+
- `address_summary` — balance and recent activity for an address
|
|
71
|
+
- `block_summary` — block details plus transaction counts
|
|
72
|
+
|
|
73
|
+
## What it isn't
|
|
74
|
+
|
|
75
|
+
- A wallet. No private keys, no signing, no `push_tx`.
|
|
76
|
+
- A full node. Talks to public coinset.org RPC.
|
|
77
|
+
- A mempool watcher. Snapshot data only.
|
|
78
|
+
|
|
79
|
+
## License
|
|
80
|
+
|
|
81
|
+
MIT
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare const MOJOS_PER_XCH = 1000000000000n;
|
|
2
|
+
export declare function toBigInt(v: bigint | number | string): bigint;
|
|
3
|
+
export declare function mojoToXch(mojo: bigint | number | string): string;
|
|
4
|
+
export interface NetspaceFormat {
|
|
5
|
+
bytes: string;
|
|
6
|
+
eib: string;
|
|
7
|
+
pib: string;
|
|
8
|
+
tib: string;
|
|
9
|
+
human: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function formatNetspace(bytes: bigint | number | string): NetspaceFormat;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export const MOJOS_PER_XCH = 1000000000000n;
|
|
2
|
+
export function toBigInt(v) {
|
|
3
|
+
if (typeof v === 'bigint')
|
|
4
|
+
return v;
|
|
5
|
+
if (typeof v === 'number') {
|
|
6
|
+
if (!Number.isInteger(v))
|
|
7
|
+
throw new Error(`expected integer, got ${v}`);
|
|
8
|
+
return BigInt(v);
|
|
9
|
+
}
|
|
10
|
+
return BigInt(v);
|
|
11
|
+
}
|
|
12
|
+
export function mojoToXch(mojo) {
|
|
13
|
+
const v = toBigInt(mojo);
|
|
14
|
+
const neg = v < 0n;
|
|
15
|
+
const abs = neg ? -v : v;
|
|
16
|
+
const whole = abs / MOJOS_PER_XCH;
|
|
17
|
+
const frac = abs % MOJOS_PER_XCH;
|
|
18
|
+
if (frac === 0n)
|
|
19
|
+
return `${neg ? '-' : ''}${whole.toString()}`;
|
|
20
|
+
let fracStr = frac.toString().padStart(12, '0').replace(/0+$/, '');
|
|
21
|
+
if (fracStr === '')
|
|
22
|
+
fracStr = '0';
|
|
23
|
+
return `${neg ? '-' : ''}${whole.toString()}.${fracStr}`;
|
|
24
|
+
}
|
|
25
|
+
const EIB = 2n ** 60n;
|
|
26
|
+
const PIB = 2n ** 50n;
|
|
27
|
+
const TIB = 2n ** 40n;
|
|
28
|
+
function bigIntDivToDecimal(num, denom, decimals) {
|
|
29
|
+
if (denom === 0n)
|
|
30
|
+
throw new Error('division by zero');
|
|
31
|
+
const scale = 10n ** BigInt(decimals);
|
|
32
|
+
const scaled = (num * scale) / denom;
|
|
33
|
+
const whole = scaled / scale;
|
|
34
|
+
const frac = scaled % scale;
|
|
35
|
+
const fracStr = frac.toString().padStart(decimals, '0').replace(/0+$/, '');
|
|
36
|
+
return fracStr === '' ? whole.toString() : `${whole.toString()}.${fracStr}`;
|
|
37
|
+
}
|
|
38
|
+
export function formatNetspace(bytes) {
|
|
39
|
+
const v = toBigInt(bytes);
|
|
40
|
+
const eib = bigIntDivToDecimal(v, EIB, 3);
|
|
41
|
+
const pib = bigIntDivToDecimal(v, PIB, 3);
|
|
42
|
+
const tib = bigIntDivToDecimal(v, TIB, 3);
|
|
43
|
+
let human;
|
|
44
|
+
if (v >= EIB)
|
|
45
|
+
human = `${eib} EiB`;
|
|
46
|
+
else if (v >= PIB)
|
|
47
|
+
human = `${pib} PiB`;
|
|
48
|
+
else
|
|
49
|
+
human = `${tib} TiB`;
|
|
50
|
+
return { bytes: v.toString(), eib, pib, tib, human };
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=amounts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"amounts.js","sourceRoot":"","sources":["../../src/chia/amounts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Network } from '../network.js';
|
|
2
|
+
export declare function puzzleHashToAddress(puzzleHashHex: string, network: Network): string;
|
|
3
|
+
export interface DecodedAddress {
|
|
4
|
+
puzzleHash: string;
|
|
5
|
+
network: Network;
|
|
6
|
+
}
|
|
7
|
+
export declare function addressToPuzzleHash(address: string): DecodedAddress;
|
|
8
|
+
export declare function looksLikeAddress(s: string): boolean;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { bech32m } from '@scure/base';
|
|
2
|
+
import { ADDRESS_PREFIX, networkFromPrefix } from '../network.js';
|
|
3
|
+
import { bytesToHex, hexToBytes } from './hex.js';
|
|
4
|
+
export function puzzleHashToAddress(puzzleHashHex, network) {
|
|
5
|
+
const ph = hexToBytes(puzzleHashHex);
|
|
6
|
+
if (ph.length !== 32)
|
|
7
|
+
throw new Error('puzzle hash must be 32 bytes');
|
|
8
|
+
return bech32m.encode(ADDRESS_PREFIX[network], bech32m.toWords(ph));
|
|
9
|
+
}
|
|
10
|
+
export function addressToPuzzleHash(address) {
|
|
11
|
+
let decoded;
|
|
12
|
+
try {
|
|
13
|
+
decoded = bech32m.decode(address);
|
|
14
|
+
}
|
|
15
|
+
catch (err) {
|
|
16
|
+
throw new Error(`invalid bech32m address: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
17
|
+
}
|
|
18
|
+
const network = networkFromPrefix(decoded.prefix);
|
|
19
|
+
if (!network) {
|
|
20
|
+
throw new Error(`unknown address prefix '${decoded.prefix}' — expected 'xch' (mainnet) or 'txch' (testnet11)`);
|
|
21
|
+
}
|
|
22
|
+
const bytes = bech32m.fromWords(decoded.words);
|
|
23
|
+
if (bytes.length !== 32) {
|
|
24
|
+
throw new Error(`decoded payload must be 32 bytes, got ${bytes.length}`);
|
|
25
|
+
}
|
|
26
|
+
return { puzzleHash: bytesToHex(bytes), network };
|
|
27
|
+
}
|
|
28
|
+
export function looksLikeAddress(s) {
|
|
29
|
+
return /^(xch|txch)1[02-9ac-hj-np-z]+$/i.test(s);
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=bech32.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bech32.js","sourceRoot":"","sources":["../../src/chia/bech32.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,cAAc,EAAW,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAC3E,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAElD,MAAM,UAAU,mBAAmB,CAAC,aAAqB,EAAE,OAAgB;IACzE,MAAM,EAAE,GAAG,UAAU,CAAC,aAAa,CAAC,CAAC;IACrC,IAAI,EAAE,CAAC,MAAM,KAAK,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IACtE,OAAO,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;AACtE,CAAC;AAOD,MAAM,UAAU,mBAAmB,CAAC,OAAe;IACjD,IAAI,OAAO,CAAC;IACZ,IAAI,CAAC;QACH,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,OAAgC,CAAC,CAAC;IAC7D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACb,4BAA4B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAC9E,EAAE,KAAK,EAAE,GAAG,EAAE,CACf,CAAC;IACJ,CAAC;IACD,MAAM,OAAO,GAAG,iBAAiB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAClD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACb,2BAA2B,OAAO,CAAC,MAAM,oDAAoD,CAC9F,CAAC;IACJ,CAAC;IACD,MAAM,KAAK,GAAG,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC/C,IAAI,KAAK,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,yCAAyC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;IAC3E,CAAC;IACD,OAAO,EAAE,UAAU,EAAE,UAAU,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,CAAS;IACxC,OAAO,iCAAiC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACnD,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Big-endian minimal-length two's-complement encoding of a non-negative bigint,
|
|
3
|
+
* matching Chia's int_to_bytes serialization used in coin name calculation.
|
|
4
|
+
*
|
|
5
|
+
* Why: coin name = sha256(parent_coin_info || puzzle_hash || amount_bytes) where
|
|
6
|
+
* amount_bytes follows Python's int.to_bytes(signed=True) with the minimum length
|
|
7
|
+
* needed. Off-by-one here produces wrong coin names.
|
|
8
|
+
*/
|
|
9
|
+
export declare function bigIntToBytes(n: bigint): Uint8Array;
|
|
10
|
+
export interface CoinFields {
|
|
11
|
+
parent_coin_info: string;
|
|
12
|
+
puzzle_hash: string;
|
|
13
|
+
amount: bigint | number | string;
|
|
14
|
+
}
|
|
15
|
+
export declare function coinName(coin: CoinFields): string;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { bytesToHex, hexToBytes } from './hex.js';
|
|
3
|
+
/**
|
|
4
|
+
* Big-endian minimal-length two's-complement encoding of a non-negative bigint,
|
|
5
|
+
* matching Chia's int_to_bytes serialization used in coin name calculation.
|
|
6
|
+
*
|
|
7
|
+
* Why: coin name = sha256(parent_coin_info || puzzle_hash || amount_bytes) where
|
|
8
|
+
* amount_bytes follows Python's int.to_bytes(signed=True) with the minimum length
|
|
9
|
+
* needed. Off-by-one here produces wrong coin names.
|
|
10
|
+
*/
|
|
11
|
+
export function bigIntToBytes(n) {
|
|
12
|
+
if (n < 0n)
|
|
13
|
+
throw new Error('coin amount must be non-negative');
|
|
14
|
+
if (n === 0n)
|
|
15
|
+
return new Uint8Array(0);
|
|
16
|
+
const buf = [];
|
|
17
|
+
let v = n;
|
|
18
|
+
while (v > 0n) {
|
|
19
|
+
buf.unshift(Number(v & 0xffn));
|
|
20
|
+
v >>= 8n;
|
|
21
|
+
}
|
|
22
|
+
if ((buf[0] ?? 0) & 0x80)
|
|
23
|
+
buf.unshift(0);
|
|
24
|
+
return Uint8Array.from(buf);
|
|
25
|
+
}
|
|
26
|
+
export function coinName(coin) {
|
|
27
|
+
const parent = hexToBytes(coin.parent_coin_info);
|
|
28
|
+
const ph = hexToBytes(coin.puzzle_hash);
|
|
29
|
+
if (parent.length !== 32)
|
|
30
|
+
throw new Error('parent_coin_info must be 32 bytes');
|
|
31
|
+
if (ph.length !== 32)
|
|
32
|
+
throw new Error('puzzle_hash must be 32 bytes');
|
|
33
|
+
const amount = bigIntToBytes(BigInt(coin.amount));
|
|
34
|
+
const hash = createHash('sha256');
|
|
35
|
+
hash.update(parent);
|
|
36
|
+
hash.update(ph);
|
|
37
|
+
hash.update(amount);
|
|
38
|
+
return bytesToHex(new Uint8Array(hash.digest()));
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=coin-name.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coin-name.js","sourceRoot":"","sources":["../../src/chia/coin-name.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAElD;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAAC,CAAS;IACrC,IAAI,CAAC,GAAG,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;IAChE,IAAI,CAAC,KAAK,EAAE;QAAE,OAAO,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;IACvC,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC;QACd,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC;QAC/B,CAAC,KAAK,EAAE,CAAC;IACX,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI;QAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IACzC,OAAO,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC9B,CAAC;AAQD,MAAM,UAAU,QAAQ,CAAC,IAAgB;IACvC,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IACjD,MAAM,EAAE,GAAG,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACxC,IAAI,MAAM,CAAC,MAAM,KAAK,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;IAC/E,IAAI,EAAE,CAAC,MAAM,KAAK,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IACtE,MAAM,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IAClD,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IAClC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACpB,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAChB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACpB,OAAO,UAAU,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;AACnD,CAAC"}
|
package/dist/chia/hex.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export function stripHexPrefix(s) {
|
|
2
|
+
return s.startsWith('0x') || s.startsWith('0X') ? s.slice(2) : s;
|
|
3
|
+
}
|
|
4
|
+
export function hexToBytes(hex) {
|
|
5
|
+
const stripped = stripHexPrefix(hex);
|
|
6
|
+
if (stripped.length % 2 !== 0)
|
|
7
|
+
throw new Error('hex string has odd length');
|
|
8
|
+
if (!/^[0-9a-fA-F]*$/.test(stripped))
|
|
9
|
+
throw new Error('hex string contains non-hex characters');
|
|
10
|
+
const out = new Uint8Array(stripped.length / 2);
|
|
11
|
+
for (let i = 0; i < out.length; i++) {
|
|
12
|
+
out[i] = parseInt(stripped.slice(i * 2, i * 2 + 2), 16);
|
|
13
|
+
}
|
|
14
|
+
return out;
|
|
15
|
+
}
|
|
16
|
+
export function bytesToHex(bytes) {
|
|
17
|
+
let s = '';
|
|
18
|
+
for (const b of bytes)
|
|
19
|
+
s += b.toString(16).padStart(2, '0');
|
|
20
|
+
return s;
|
|
21
|
+
}
|
|
22
|
+
export function isHex32(s) {
|
|
23
|
+
const stripped = stripHexPrefix(s);
|
|
24
|
+
return stripped.length === 64 && /^[0-9a-fA-F]+$/.test(stripped);
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=hex.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hex.js","sourceRoot":"","sources":["../../src/chia/hex.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,cAAc,CAAC,CAAS;IACtC,OAAO,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,MAAM,QAAQ,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;IACrC,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;IAC5E,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,QAAQ,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;IAChG,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAChD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACpC,GAAG,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC1D,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAiB;IAC1C,IAAI,CAAC,GAAG,EAAE,CAAC;IACX,KAAK,MAAM,CAAC,IAAI,KAAK;QAAE,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAC5D,OAAO,CAAC,CAAC;AACX,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,CAAS;IAC/B,MAAM,QAAQ,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC;IACnC,OAAO,QAAQ,CAAC,MAAM,KAAK,EAAE,IAAI,gBAAgB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AACnE,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { RPCAgent } from 'chia-agent';
|
|
2
|
+
import { Network } from '../network.js';
|
|
3
|
+
export declare function getAgent(network: Network): RPCAgent;
|
|
4
|
+
/** Test-only hook to inject a stub agent. */
|
|
5
|
+
export declare function setAgent(network: Network, agent: RPCAgent): void;
|
|
6
|
+
/** Test-only hook to clear cached agents. */
|
|
7
|
+
export declare function resetAgents(): void;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { RPCAgent } from 'chia-agent';
|
|
2
|
+
import { COINSET_HOST } from '../network.js';
|
|
3
|
+
const cache = new Map();
|
|
4
|
+
export function getAgent(network) {
|
|
5
|
+
let agent = cache.get(network);
|
|
6
|
+
if (!agent) {
|
|
7
|
+
agent = new RPCAgent({
|
|
8
|
+
protocol: 'https',
|
|
9
|
+
host: COINSET_HOST[network],
|
|
10
|
+
port: 443,
|
|
11
|
+
keepAlive: true,
|
|
12
|
+
timeout: 30_000,
|
|
13
|
+
});
|
|
14
|
+
cache.set(network, agent);
|
|
15
|
+
}
|
|
16
|
+
return agent;
|
|
17
|
+
}
|
|
18
|
+
/** Test-only hook to inject a stub agent. */
|
|
19
|
+
export function setAgent(network, agent) {
|
|
20
|
+
cache.set(network, agent);
|
|
21
|
+
}
|
|
22
|
+
/** Test-only hook to clear cached agents. */
|
|
23
|
+
export function resetAgents() {
|
|
24
|
+
cache.clear();
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=agent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../../src/coinset/agent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,YAAY,EAAW,MAAM,eAAe,CAAC;AAEtD,MAAM,KAAK,GAAG,IAAI,GAAG,EAAqB,CAAC;AAE3C,MAAM,UAAU,QAAQ,CAAC,OAAgB;IACvC,IAAI,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC/B,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,KAAK,GAAG,IAAI,QAAQ,CAAC;YACnB,QAAQ,EAAE,OAAO;YACjB,IAAI,EAAE,YAAY,CAAC,OAAO,CAAC;YAC3B,IAAI,EAAE,GAAG;YACT,SAAS,EAAE,IAAI;YACf,OAAO,EAAE,MAAM;SAChB,CAAC,CAAC;QACH,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAC5B,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,6CAA6C;AAC7C,MAAM,UAAU,QAAQ,CAAC,OAAgB,EAAE,KAAe;IACxD,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;AAC5B,CAAC;AAED,6CAA6C;AAC7C,MAAM,UAAU,WAAW;IACzB,KAAK,CAAC,KAAK,EAAE,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { RPCAgent } from 'chia-agent';
|
|
2
|
+
export interface CoinRecordLike {
|
|
3
|
+
coin: {
|
|
4
|
+
parent_coin_info: string;
|
|
5
|
+
puzzle_hash: string;
|
|
6
|
+
amount: bigint | number | string;
|
|
7
|
+
};
|
|
8
|
+
confirmed_block_index: number;
|
|
9
|
+
spent_block_index: number;
|
|
10
|
+
coinbase: boolean;
|
|
11
|
+
timestamp: bigint | number;
|
|
12
|
+
spent?: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Fetch coin records by puzzle hash, paginating by height window if the response
|
|
16
|
+
* looks capped. Coinset returns up to a fixed number of records per call; when
|
|
17
|
+
* we see exactly that many, we re-query the range above the highest confirmed
|
|
18
|
+
* height we've seen so far.
|
|
19
|
+
*/
|
|
20
|
+
export declare function fetchCoinRecordsByPuzzleHash(agent: RPCAgent, puzzleHashHex: string, options?: {
|
|
21
|
+
includeSpent?: boolean;
|
|
22
|
+
startHeight?: number;
|
|
23
|
+
endHeight?: number;
|
|
24
|
+
}): Promise<CoinRecordLike[]>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { get_coin_records_by_puzzle_hash } from 'chia-agent/api/rpc/full_node/index.js';
|
|
2
|
+
const MAX_HEIGHT = 0xffffffff;
|
|
3
|
+
const PAGE_SIZE_LIMIT = 5_000;
|
|
4
|
+
/**
|
|
5
|
+
* Fetch coin records by puzzle hash, paginating by height window if the response
|
|
6
|
+
* looks capped. Coinset returns up to a fixed number of records per call; when
|
|
7
|
+
* we see exactly that many, we re-query the range above the highest confirmed
|
|
8
|
+
* height we've seen so far.
|
|
9
|
+
*/
|
|
10
|
+
export async function fetchCoinRecordsByPuzzleHash(agent, puzzleHashHex, options = {}) {
|
|
11
|
+
const includeSpent = options.includeSpent ?? false;
|
|
12
|
+
let start = options.startHeight ?? 0;
|
|
13
|
+
const end = options.endHeight ?? MAX_HEIGHT;
|
|
14
|
+
const all = [];
|
|
15
|
+
const seen = new Set();
|
|
16
|
+
while (start <= end) {
|
|
17
|
+
const res = await get_coin_records_by_puzzle_hash(agent, {
|
|
18
|
+
puzzle_hash: puzzleHashHex,
|
|
19
|
+
start_height: start,
|
|
20
|
+
end_height: end,
|
|
21
|
+
include_spent_coins: includeSpent,
|
|
22
|
+
});
|
|
23
|
+
const records = (res.coin_records ?? []);
|
|
24
|
+
let maxHeight = start;
|
|
25
|
+
for (const r of records) {
|
|
26
|
+
const key = `${r.coin.parent_coin_info}|${r.coin.puzzle_hash}|${r.coin.amount.toString()}`;
|
|
27
|
+
if (seen.has(key))
|
|
28
|
+
continue;
|
|
29
|
+
seen.add(key);
|
|
30
|
+
all.push(r);
|
|
31
|
+
if (r.confirmed_block_index > maxHeight)
|
|
32
|
+
maxHeight = r.confirmed_block_index;
|
|
33
|
+
}
|
|
34
|
+
if (records.length < PAGE_SIZE_LIMIT)
|
|
35
|
+
break;
|
|
36
|
+
if (maxHeight <= start)
|
|
37
|
+
break;
|
|
38
|
+
start = maxHeight + 1;
|
|
39
|
+
}
|
|
40
|
+
return all;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=pagination.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pagination.js","sourceRoot":"","sources":["../../src/coinset/pagination.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,+BAA+B,EAAE,MAAM,uCAAuC,CAAC;AAexF,MAAM,UAAU,GAAG,UAAU,CAAC;AAC9B,MAAM,eAAe,GAAG,KAAK,CAAC;AAE9B;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAChD,KAAe,EACf,aAAqB,EACrB,UAAgF,EAAE;IAElF,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,KAAK,CAAC;IACnD,IAAI,KAAK,GAAG,OAAO,CAAC,WAAW,IAAI,CAAC,CAAC;IACrC,MAAM,GAAG,GAAG,OAAO,CAAC,SAAS,IAAI,UAAU,CAAC;IAC5C,MAAM,GAAG,GAAqB,EAAE,CAAC;IACjC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAE/B,OAAO,KAAK,IAAI,GAAG,EAAE,CAAC;QACpB,MAAM,GAAG,GAAG,MAAM,+BAA+B,CAAC,KAAK,EAAE;YACvD,WAAW,EAAE,aAAa;YAC1B,YAAY,EAAE,KAAK;YACnB,UAAU,EAAE,GAAG;YACf,mBAAmB,EAAE,YAAY;SAClC,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,YAAY,IAAI,EAAE,CAAqB,CAAC;QAC7D,IAAI,SAAS,GAAG,KAAK,CAAC;QACtB,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YACxB,MAAM,GAAG,GAAG,GAAG,CAAC,CAAC,IAAI,CAAC,gBAAgB,IAAI,CAAC,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;YAC3F,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,SAAS;YAC5B,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACd,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACZ,IAAI,CAAC,CAAC,qBAAqB,GAAG,SAAS;gBAAE,SAAS,GAAG,CAAC,CAAC,qBAAqB,CAAC;QAC/E,CAAC;QACD,IAAI,OAAO,CAAC,MAAM,GAAG,eAAe;YAAE,MAAM;QAC5C,IAAI,SAAS,IAAI,KAAK;YAAE,MAAM;QAC9B,KAAK,GAAG,SAAS,GAAG,CAAC,CAAC;IACxB,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
+
import { createServer } from './server.js';
|
|
4
|
+
const server = createServer();
|
|
5
|
+
const transport = new StdioServerTransport();
|
|
6
|
+
await server.connect(transport);
|
|
7
|
+
const shutdown = () => {
|
|
8
|
+
void server.close().then(() => process.exit(0));
|
|
9
|
+
};
|
|
10
|
+
process.on('SIGINT', shutdown);
|
|
11
|
+
process.on('SIGTERM', shutdown);
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;AAC9B,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;AAE7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAEhC,MAAM,QAAQ,GAAG,GAAS,EAAE;IAC1B,KAAK,MAAM,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AAClD,CAAC,CAAC;AACF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;AAC/B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type Network = 'mainnet' | 'testnet11';
|
|
2
|
+
export declare const NETWORKS: readonly Network[];
|
|
3
|
+
export declare const DEFAULT_NETWORK: Network;
|
|
4
|
+
export declare const COINSET_HOST: Record<Network, string>;
|
|
5
|
+
export declare const ADDRESS_PREFIX: Record<Network, string>;
|
|
6
|
+
export declare function networkFromPrefix(hrp: string): Network | null;
|
package/dist/network.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const NETWORKS = ['mainnet', 'testnet11'];
|
|
2
|
+
export const DEFAULT_NETWORK = 'mainnet';
|
|
3
|
+
export const COINSET_HOST = {
|
|
4
|
+
mainnet: 'api.coinset.org',
|
|
5
|
+
testnet11: 'api-testnet11.coinset.org',
|
|
6
|
+
};
|
|
7
|
+
export const ADDRESS_PREFIX = {
|
|
8
|
+
mainnet: 'xch',
|
|
9
|
+
testnet11: 'txch',
|
|
10
|
+
};
|
|
11
|
+
export function networkFromPrefix(hrp) {
|
|
12
|
+
if (hrp === 'xch')
|
|
13
|
+
return 'mainnet';
|
|
14
|
+
if (hrp === 'txch')
|
|
15
|
+
return 'testnet11';
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=network.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"network.js","sourceRoot":"","sources":["../src/network.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,QAAQ,GAAuB,CAAC,SAAS,EAAE,WAAW,CAAU,CAAC;AAE9E,MAAM,CAAC,MAAM,eAAe,GAAY,SAAS,CAAC;AAElD,MAAM,CAAC,MAAM,YAAY,GAA4B;IACnD,OAAO,EAAE,iBAAiB;IAC1B,SAAS,EAAE,2BAA2B;CACvC,CAAC;AAEF,MAAM,CAAC,MAAM,cAAc,GAA4B;IACrD,OAAO,EAAE,KAAK;IACd,SAAS,EAAE,MAAM;CAClB,CAAC;AAEF,MAAM,UAAU,iBAAiB,CAAC,GAAW;IAC3C,IAAI,GAAG,KAAK,KAAK;QAAE,OAAO,SAAS,CAAC;IACpC,IAAI,GAAG,KAAK,MAAM;QAAE,OAAO,WAAW,CAAC;IACvC,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export function register(server) {
|
|
3
|
+
server.registerPrompt('address_summary', {
|
|
4
|
+
title: 'Chia address summary',
|
|
5
|
+
description: 'Balance and recent activity for a Chia address or puzzle hash.',
|
|
6
|
+
argsSchema: {
|
|
7
|
+
address: z.string().describe('An xch/txch bech32m address OR a 32-byte hex puzzle hash.'),
|
|
8
|
+
network: z
|
|
9
|
+
.enum(['mainnet', 'testnet11'])
|
|
10
|
+
.optional()
|
|
11
|
+
.describe('Override network. Auto-detected from address prefix when omitted.'),
|
|
12
|
+
},
|
|
13
|
+
}, ({ address, network }) => {
|
|
14
|
+
const lines = [
|
|
15
|
+
`Summarise the address ${address} on the Chia blockchain.`,
|
|
16
|
+
``,
|
|
17
|
+
`Steps:`,
|
|
18
|
+
`1. If the input looks like a bech32m address (starts with xch1 or txch1), call \`address_to_puzzle_hash\` to confirm the puzzle hash and detected network. If it is a hex puzzle hash, call \`puzzle_hash_to_address\` with the appropriate network to produce the address.`,
|
|
19
|
+
`2. Call \`get_balance\` with \`address_or_puzzle_hash: "${address}"\`${network ? ` and \`network: "${network}"\`` : ''}.`,
|
|
20
|
+
`3. Call \`get_coin_records_by_puzzle_hash\` with the same input, \`include_spent_coins: false\`, to fetch unspent coins. If the count is large, show only the 10 highest-value coins.`,
|
|
21
|
+
`4. Present:`,
|
|
22
|
+
` - Network and address (canonical form).`,
|
|
23
|
+
` - Puzzle hash.`,
|
|
24
|
+
` - Balance in XCH and mojos.`,
|
|
25
|
+
` - Number of unspent coins.`,
|
|
26
|
+
` - Top coins by amount (coin amount in XCH, confirmed_block_index).`,
|
|
27
|
+
`5. Keep the summary short. Use plain prose, no tables unless the user asks.`,
|
|
28
|
+
];
|
|
29
|
+
return {
|
|
30
|
+
messages: [{ role: 'user', content: { type: 'text', text: lines.join('\n') } }],
|
|
31
|
+
};
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=address-summary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"address-summary.js","sourceRoot":"","sources":["../../src/prompts/address-summary.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,UAAU,QAAQ,CAAC,MAAiB;IACxC,MAAM,CAAC,cAAc,CACnB,iBAAiB,EACjB;QACE,KAAK,EAAE,sBAAsB;QAC7B,WAAW,EAAE,gEAAgE;QAC7E,UAAU,EAAE;YACV,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,2DAA2D,CAAC;YACzF,OAAO,EAAE,CAAC;iBACP,IAAI,CAAC,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;iBAC9B,QAAQ,EAAE;iBACV,QAAQ,CAAC,mEAAmE,CAAC;SACjF;KACF,EACD,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE;QACvB,MAAM,KAAK,GAAG;YACZ,yBAAyB,OAAO,0BAA0B;YAC1D,EAAE;YACF,QAAQ;YACR,6QAA6Q;YAC7Q,2DAA2D,OAAO,MAAM,OAAO,CAAC,CAAC,CAAC,oBAAoB,OAAO,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG;YAC1H,uLAAuL;YACvL,aAAa;YACb,4CAA4C;YAC5C,mBAAmB;YACnB,gCAAgC;YAChC,+BAA+B;YAC/B,uEAAuE;YACvE,6EAA6E;SAC9E,CAAC;QACF,OAAO;YACL,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;SAChF,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export function register(server) {
|
|
3
|
+
server.registerPrompt('block_summary', {
|
|
4
|
+
title: 'Chia block summary',
|
|
5
|
+
description: 'Block details plus transaction counts, by height or header hash.',
|
|
6
|
+
argsSchema: {
|
|
7
|
+
height: z
|
|
8
|
+
.string()
|
|
9
|
+
.optional()
|
|
10
|
+
.describe('Block height (as a string — MCP prompt args are strings).'),
|
|
11
|
+
header_hash: z
|
|
12
|
+
.string()
|
|
13
|
+
.optional()
|
|
14
|
+
.describe('Block header hash (32-byte hex, optional 0x prefix).'),
|
|
15
|
+
network: z
|
|
16
|
+
.enum(['mainnet', 'testnet11'])
|
|
17
|
+
.optional()
|
|
18
|
+
.describe('Network. Defaults to mainnet.'),
|
|
19
|
+
},
|
|
20
|
+
}, ({ height, header_hash, network }) => {
|
|
21
|
+
const net = network ?? 'mainnet';
|
|
22
|
+
const lookupCall = header_hash
|
|
23
|
+
? `\`get_block_by_hash\` with \`header_hash: "${header_hash}"\` and \`network: "${net}"\``
|
|
24
|
+
: height !== undefined
|
|
25
|
+
? `\`get_block_by_height\` with \`height: ${height}\` and \`network: "${net}"\``
|
|
26
|
+
: `either \`get_block_by_height\` (if a height is supplied) or \`get_block_by_hash\` (if a header hash is supplied)`;
|
|
27
|
+
const countCall = header_hash
|
|
28
|
+
? `\`count_block_transactions\` with \`header_hash: "${header_hash}"\` and \`network: "${net}"\``
|
|
29
|
+
: height !== undefined
|
|
30
|
+
? `\`count_block_transactions\` with \`height: ${height}\` and \`network: "${net}"\``
|
|
31
|
+
: `\`count_block_transactions\` with the matching identifier and \`network: "${net}"\``;
|
|
32
|
+
const text = [
|
|
33
|
+
`Summarise the Chia ${net} block.`,
|
|
34
|
+
``,
|
|
35
|
+
`Steps:`,
|
|
36
|
+
`1. Fetch the block record via ${lookupCall}.`,
|
|
37
|
+
`2. Fetch transaction counts via ${countCall}.`,
|
|
38
|
+
`3. Present:`,
|
|
39
|
+
` - Height and header hash (and previous hash).`,
|
|
40
|
+
` - Timestamp (UTC). If null, note that this is not a transaction block.`,
|
|
41
|
+
` - is_transaction_block.`,
|
|
42
|
+
` - coin_spends_count, additions_count, removals_count.`,
|
|
43
|
+
`4. Keep it short.`,
|
|
44
|
+
].join('\n');
|
|
45
|
+
return {
|
|
46
|
+
messages: [{ role: 'user', content: { type: 'text', text } }],
|
|
47
|
+
};
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=block-summary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"block-summary.js","sourceRoot":"","sources":["../../src/prompts/block-summary.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,UAAU,QAAQ,CAAC,MAAiB;IACxC,MAAM,CAAC,cAAc,CACnB,eAAe,EACf;QACE,KAAK,EAAE,oBAAoB;QAC3B,WAAW,EAAE,kEAAkE;QAC/E,UAAU,EAAE;YACV,MAAM,EAAE,CAAC;iBACN,MAAM,EAAE;iBACR,QAAQ,EAAE;iBACV,QAAQ,CAAC,2DAA2D,CAAC;YACxE,WAAW,EAAE,CAAC;iBACX,MAAM,EAAE;iBACR,QAAQ,EAAE;iBACV,QAAQ,CAAC,sDAAsD,CAAC;YACnE,OAAO,EAAE,CAAC;iBACP,IAAI,CAAC,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;iBAC9B,QAAQ,EAAE;iBACV,QAAQ,CAAC,+BAA+B,CAAC;SAC7C;KACF,EACD,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,EAAE,EAAE;QACnC,MAAM,GAAG,GAAG,OAAO,IAAI,SAAS,CAAC;QACjC,MAAM,UAAU,GAAG,WAAW;YAC5B,CAAC,CAAC,8CAA8C,WAAW,uBAAuB,GAAG,KAAK;YAC1F,CAAC,CAAC,MAAM,KAAK,SAAS;gBACpB,CAAC,CAAC,0CAA0C,MAAM,sBAAsB,GAAG,KAAK;gBAChF,CAAC,CAAC,kHAAkH,CAAC;QAEzH,MAAM,SAAS,GAAG,WAAW;YAC3B,CAAC,CAAC,qDAAqD,WAAW,uBAAuB,GAAG,KAAK;YACjG,CAAC,CAAC,MAAM,KAAK,SAAS;gBACpB,CAAC,CAAC,+CAA+C,MAAM,sBAAsB,GAAG,KAAK;gBACrF,CAAC,CAAC,6EAA6E,GAAG,KAAK,CAAC;QAE5F,MAAM,IAAI,GAAG;YACX,sBAAsB,GAAG,SAAS;YAClC,EAAE;YACF,QAAQ;YACR,iCAAiC,UAAU,GAAG;YAC9C,mCAAmC,SAAS,GAAG;YAC/C,aAAa;YACb,kDAAkD;YAClD,2EAA2E;YAC3E,4BAA4B;YAC5B,0DAA0D;YAC1D,mBAAmB;SACpB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEb,OAAO;YACL,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC;SAC9D,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export function register(server) {
|
|
3
|
+
server.registerPrompt('network_status', {
|
|
4
|
+
title: 'Chia network status',
|
|
5
|
+
description: 'Summarise the current state of the Chia blockchain: peak height, netspace, sync, difficulty.',
|
|
6
|
+
argsSchema: {
|
|
7
|
+
network: z
|
|
8
|
+
.enum(['mainnet', 'testnet11'])
|
|
9
|
+
.optional()
|
|
10
|
+
.describe('Network to query. Defaults to mainnet.'),
|
|
11
|
+
},
|
|
12
|
+
}, ({ network }) => {
|
|
13
|
+
const net = network ?? 'mainnet';
|
|
14
|
+
const text = `Summarise the current state of the Chia ${net} blockchain.\n\n` +
|
|
15
|
+
`Steps:\n` +
|
|
16
|
+
`1. Call \`get_blockchain_state\` with \`network: "${net}"\`.\n` +
|
|
17
|
+
`2. From the response present, in this order:\n` +
|
|
18
|
+
` - Peak height and the time of that block (timestamp, as UTC).\n` +
|
|
19
|
+
` - Netspace in EiB (use \`space_eib\` plus a "PiB" fallback if it is less than 1 EiB).\n` +
|
|
20
|
+
` - Difficulty and sub_slot_iters.\n` +
|
|
21
|
+
` - Sync status: synced yes/no, plus sync_tip_height and sync_progress_height if not synced.\n` +
|
|
22
|
+
` - Mempool size and cost.\n` +
|
|
23
|
+
`3. Keep the summary short and scannable. One short line per item.`;
|
|
24
|
+
return {
|
|
25
|
+
messages: [{ role: 'user', content: { type: 'text', text } }],
|
|
26
|
+
};
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=network-status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"network-status.js","sourceRoot":"","sources":["../../src/prompts/network-status.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,UAAU,QAAQ,CAAC,MAAiB;IACxC,MAAM,CAAC,cAAc,CACnB,gBAAgB,EAChB;QACE,KAAK,EAAE,qBAAqB;QAC5B,WAAW,EACT,8FAA8F;QAChG,UAAU,EAAE;YACV,OAAO,EAAE,CAAC;iBACP,IAAI,CAAC,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;iBAC9B,QAAQ,EAAE;iBACV,QAAQ,CAAC,wCAAwC,CAAC;SACtD;KACF,EACD,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE;QACd,MAAM,GAAG,GAAG,OAAO,IAAI,SAAS,CAAC;QACjC,MAAM,IAAI,GACR,2CAA2C,GAAG,kBAAkB;YAChE,UAAU;YACV,qDAAqD,GAAG,QAAQ;YAChE,gDAAgD;YAChD,oEAAoE;YACpE,4FAA4F;YAC5F,uCAAuC;YACvC,iGAAiG;YACjG,+BAA+B;YAC/B,mEAAmE,CAAC;QACtE,OAAO;YACL,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC;SAC9D,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
|