Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 176 additions & 0 deletions packages/shinzohub/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
# @shinzo/shinzohub

Viem-first TypeScript client actions for ShinzoHub.

This package intentionally keeps the public API small:

- query registered views
- create a view from a viewbundle
- convert Shinzo and EVM address formats
- reuse ShinzoHub Viem chain definitions

## Usage

```ts
import { createPublicClient, createWalletClient, http } from "viem";
import {
createShinzoHubClient,
shinzoHubActions,
shinzoHubDevelop,
} from "@shinzo/shinzohub";

const publicClient = createPublicClient({
chain: shinzoHubDevelop,
transport: http(),
}).extend(shinzoHubActions);

const views = await publicClient.listViews({
limit: 25,
includeMetadata: true,
});

const view = await publicClient.getView({
address: views.views[0].contractAddress,
});
```

Create a view with a wallet client:

```ts
const walletClient = createShinzoHubClient(
createWalletClient({
account: "0x1234567890AbcdEF1234567890aBcdef12345678",
chain: shinzoHubDevelop,
transport: http(),
}),
);

const hash = await walletClient.createView({
bundle: "0x68656c6c6f",
});
```

Read the deployed View address from the receipt:

```ts
import { getCreatedViewAddress } from "@shinzo/shinzohub";

const receipt = await publicClient.waitForTransactionReceipt({ hash });
const viewAddress = getCreatedViewAddress(receipt);
```

Use a custom pricing contract by passing `pricing`:

```ts
const hash = await walletClient.createView({
bundle: "0x68656c6c6f",
pricing: "0x0000000000000000000000000000000000000000",
});
```

Power users can import `viewRegistryAbi` and `viewRegistryAddress` and call Viem directly for lower-level contract reads or log decoding.

## Roadmap

Checked items are covered by the current public SDK. Unchecked items are
ShinzoHub protocol surface area that should be considered in future passes.

### Client And Chains

- [x] Decorate an existing Viem client with `createShinzoHubClient`.
- [x] Use modular Viem actions with `shinzoHubActions`.
- [x] Export the local Viem chain definition as `shinzoHubLocal`.
- [x] Export the develop Viem chain definition as `shinzoHubDevelop`.
- [x] Export the devnet Viem chain definition as `shinzoHubDevnet`.
- [x] Export the testnet Viem chain definition as `shinzoHubTestnet`.
- [x] Export the mainnet Viem chain definition as `shinzoHubMainnet`.
- [x] Export known chain mappings as `shinzoHubChains`.
- [x] Keep package subpaths limited to `@shinzo/shinzohub`, `@shinzo/shinzohub/views`, `@shinzo/shinzohub/addresses`, and `@shinzo/shinzohub/chains`.
- [x] Keep `./internal`, URL builders, calldata builders, event selectors, and payload normalizers out of public package exports.

### ViewRegistry Precompile

- [x] Cover the ViewRegistry precompile address with `viewRegistryAddress`.
- [x] Cover the ViewRegistry ABI with `viewRegistryAbi`.
- [x] Cover `register(bytes)` with `createView({ bundle })`.
- [x] Cover `registerWithPricing(bytes,address)` with `createView({ bundle, pricing })`.
- [x] Cover `ViewCreated(address,address,string)` through `getCreatedViewAddress(receipt)` and `viewRegistryAbi`.
- [ ] Add an ergonomic read helper for `getView(address)` if apps need direct ViewRegistry creator reads.
- [ ] Add dedicated ViewRegistry event query/decode helpers if ABI-based Viem usage is not enough.

### Cosmos REST View Queries

- [x] Cover `GET /shinzonetwork/view/v1/views` with `listViews`.
- [x] Cover pagination limit, offset, key, total-count, and reverse options in `listViews`.
- [x] Cover `include_data`, `since_block`, and `include_metadata` in `listViews`.
- [x] Cover view filters for name and creator in `listViews`.
- [x] Cover metadata filters for root type, lens hash, query text, SDL text, and lens args text in `listViews`.
- [x] Cover `GET /shinzonetwork/view/v1/views/{contract_address}` with `getView`.
- [x] Cover `include_data` and `include_metadata` in `getView`.
- [x] Cover `GET /shinzonetwork/view/v1/view_count` with `countViews`.

### Deployed View Contracts

- [ ] Cover `name()`.
- [ ] Cover `creator()`.
- [ ] Cover `report(uint256,uint256)`.
- [ ] Cover `hosts()`.
- [ ] Cover `unhost()`.
- [ ] Cover `stake()`.
- [ ] Cover `unstake(uint256)`.
- [ ] Cover `totalStake()`.
- [ ] Cover `stakeOf(address)`.
- [ ] Cover `rate()`.
- [ ] Cover `complexity()`.
- [ ] Cover `fund(bytes)`.
- [ ] Cover `fundFor(address,bytes)`.
- [ ] Cover `fundOf(bytes)`.
- [ ] Cover `fundBy(address,bytes)`.
- [ ] Cover `defund(bytes,uint256)`.
- [ ] Cover `earnings()`.
- [ ] Cover `claimEarnings()`.
- [ ] Cover `consume(bytes)`.
- [ ] Cover `price()`.
- [ ] Cover `pricingContract()`.

### Addresses

- [x] Normalize Shinzo address input with `normalizeShinzoAddress`.
- [x] Normalize EVM hex address input with `normalizeHexAddress`.
- [x] Convert EVM hex to Shinzo bech32 with `hexToShinzoAddress`.
- [x] Convert Shinzo bech32 to EVM hex with `shinzoAddressToHex`.
- [ ] Reconsider boolean address validators only if product UI code needs them.
- [ ] Reconsider public address constants only if users need to configure custom prefixes or byte lengths often.
- [ ] Reconsider public address error classes only if consumers need typed catch branches.

### HostRegistry Precompile

- [ ] Cover the HostRegistry precompile address.
- [ ] Cover the HostRegistry ABI.
- [ ] Cover `register(bytes,bytes,bytes,string)`.
- [ ] Cover `isRegistered(address)`.
- [ ] Cover `getDid(address)`.
- [ ] Cover `getConnectionString(address)`.
- [ ] Cover `Registered(address,bytes,string)`.
- [ ] Cover Cosmos REST host queries when the REST API is stable.

### IndexerRegistry Precompile

- [ ] Cover the IndexerRegistry precompile address.
- [ ] Cover the IndexerRegistry ABI.
- [ ] Cover `register(bytes,bytes,bytes,string,string,uint64)`.
- [ ] Cover `isRegistered(address)`.
- [ ] Cover `getDid(address)`.
- [ ] Cover `getConnectionString(address)`.
- [ ] Cover `getSourceChain(address)`.
- [ ] Cover `Registered(address,bytes,string,string,uint64)`.
- [ ] Cover Cosmos REST indexer queries when the REST API is stable.
- [ ] Cover indexer assertion transaction helpers when the workflow is designed.

### SourceHub And Admin Workflows

- [ ] Cover SourceHub ICA registration transactions.
- [ ] Cover Shinzo policy transactions.
- [ ] Cover Shinzo object registration transactions.
- [ ] Cover stream access transactions.
- [ ] Cover admin params queries.
49 changes: 49 additions & 0 deletions packages/shinzohub/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"name": "@shinzo/shinzohub",
"version": "0.1.0",
"description": "Viem-first TypeScript client actions for ShinzoHub.",
"type": "module",
"sideEffects": false,
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"./addresses": {
"types": "./dist/addresses/index.d.ts",
"default": "./dist/addresses/index.js"
},
"./chains": {
"types": "./dist/chains/index.d.ts",
"default": "./dist/chains/index.js"
},
"./views": {
"types": "./dist/views/index.d.ts",
"default": "./dist/views/index.js"
},
"./package.json": "./package.json"
},
"files": [
"dist",
"README.md",
"llm.md"
],
"scripts": {
"build": "tsc -p tsconfig.build.json",
"prepack": "pnpm run build",
"test": "vitest run",
"typecheck": "tsc -p tsconfig.json"
},
"devDependencies": {
"@types/node": "^25.7.0",
"abitype": "^1.0.6",
"typescript": "^5.9.3",
"viem": "^2.21.32",
"vitest": "^3.1.1"
},
"peerDependencies": {
"abitype": "^1.0.6",
"viem": "^2.21.32"
}
}
46 changes: 46 additions & 0 deletions packages/shinzohub/src/addresses/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { describe, expect, it } from "vitest";
import {
hexToShinzoAddress,
normalizeHexAddress,
normalizeShinzoAddress,
shinzoAddressToHex,
} from "./index.js";

describe("Shinzo address utilities", () => {
it("converts a 20-byte EVM address to Shinzo bech32", () => {
expect(hexToShinzoAddress("0x0000000000000000000000000000000000000000")).toBe(
"shinzo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqcclt73",
);
expect(hexToShinzoAddress("000102030405060708090a0b0c0d0e0f10111213")).toBe(
"shinzo1qqqsyqcyq5rqwzqfpg9scrgwpugpzysngdwwg4",
);
});

it("converts Shinzo bech32 addresses back to lowercase EVM hex", () => {
expect(shinzoAddressToHex("shinzo1qqqsyqcyq5rqwzqfpg9scrgwpugpzysngdwwg4")).toBe(
"0x000102030405060708090a0b0c0d0e0f10111213",
);
});

it("normalizes bech32 and hex inputs to canonical Shinzo bech32", () => {
expect(normalizeShinzoAddress(" 0X000102030405060708090A0B0C0D0E0F10111213 ")).toBe(
"shinzo1qqqsyqcyq5rqwzqfpg9scrgwpugpzysngdwwg4",
);
expect(normalizeShinzoAddress("SHINZO1QQQSYQCYQ5RQWZQFPG9SCRGWPUGPZYSNGDWWG4")).toBe(
"shinzo1qqqsyqcyq5rqwzqfpg9scrgwpugpzysngdwwg4",
);
});

it("normalizes hex addresses without changing the address bytes", () => {
expect(normalizeHexAddress("000102030405060708090A0B0C0D0E0F10111213")).toBe(
"0x000102030405060708090a0b0c0d0e0f10111213",
);
});

it("rejects invalid prefixes and lengths", () => {
expect(() => shinzoAddressToHex("source1qqqsyqcyq5rqwzqfpg9scrgwpugpzysns0y44x")).toThrow(
Error,
);
expect(() => normalizeShinzoAddress("0x1234")).toThrow(Error);
});
});
Loading
Loading