@orbinum/sdk 0.4.2 → 0.6.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 +15 -724
- package/dist/index.d.mts +1310 -550
- package/dist/index.d.ts +1310 -550
- package/dist/index.js +1593 -496
- package/dist/index.mjs +1580 -496
- package/package.json +23 -21
package/README.md
CHANGED
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
Official TypeScript SDK for Orbinum — a privacy-focused blockchain built on Substrate with an EVM compatibility layer.
|
|
4
4
|
|
|
5
|
-
The SDK provides typed modules for interacting with the Orbinum protocol: Substrate RPC, EVM JSON-RPC, shielded pool operations, account identity and mapping, `rpc-v2`, ZK verifier, precompiles, and the indexer REST API.
|
|
6
|
-
|
|
7
5
|
## Installation
|
|
8
6
|
|
|
9
7
|
```bash
|
|
@@ -16,7 +14,6 @@ pnpm add @orbinum/sdk
|
|
|
16
14
|
|
|
17
15
|
- Node.js 18 or later
|
|
18
16
|
- A running Orbinum node (Substrate WebSocket endpoint)
|
|
19
|
-
- An EVM JSON-RPC endpoint (optional, required for EVM and precompile operations)
|
|
20
17
|
|
|
21
18
|
## Quick Start
|
|
22
19
|
|
|
@@ -25,733 +22,27 @@ import { OrbinumClient } from '@orbinum/sdk';
|
|
|
25
22
|
|
|
26
23
|
const client = await OrbinumClient.connect({
|
|
27
24
|
substrateWs: 'ws://localhost:9944',
|
|
28
|
-
evmRpc: 'http://localhost:9933',
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
const chainInfo = await client.substrate.getChainInfo();
|
|
32
|
-
const root = await client.rpcV2.privacy.getMerkleRoot();
|
|
33
|
-
const events = await client.substrate.queryBlockEvents('0xabc...');
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
`OrbinumClient` wires together the SDK surface under a single interface.
|
|
37
|
-
|
|
38
|
-
- `client.substrate`: Substrate WebSocket RPC, block queries, event decoding, and transaction helpers
|
|
39
|
-
- `client.evm`: EVM JSON-RPC client, or `null` if `evmRpc` was not configured
|
|
40
|
-
- `client.rpcV2`: typed Orbinum `rpc-v2` namespaces
|
|
41
|
-
- `client.shieldedPool`: shielded-pool extrinsics and note helpers
|
|
42
|
-
- `client.accountMapping`: alias, chain-link, metadata, and marketplace operations
|
|
43
|
-
- `client.precompiles`: EVM precompile wrappers, or `null` if `evmRpc` was not configured
|
|
44
|
-
|
|
45
|
-
Each module can also be instantiated independently.
|
|
46
|
-
|
|
47
|
-
## OrbinumClientProvider
|
|
48
|
-
|
|
49
|
-
`OrbinumClientProvider` wraps `OrbinumClient` with automatic WebSocket reconnection, typed connection status events, and a React-friendly lifecycle.
|
|
50
|
-
|
|
51
|
-
```ts
|
|
52
|
-
import { OrbinumClientProvider } from '@orbinum/sdk';
|
|
53
|
-
|
|
54
|
-
const provider = new OrbinumClientProvider({
|
|
55
|
-
substrateWs: 'ws://localhost:9944',
|
|
56
|
-
evmRpc: 'http://localhost:9933',
|
|
25
|
+
evmRpc: 'http://localhost:9933', // optional
|
|
57
26
|
});
|
|
58
27
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
await provider.connect();
|
|
64
|
-
const client = provider.client; // OrbinumClient once connected
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
## rpc-v2
|
|
68
|
-
|
|
69
|
-
Orbinum's `rpc-v2` is organized by namespace.
|
|
70
|
-
|
|
71
|
-
- Standard Frontier namespaces: `eth_*`, `net_*`, `web3_*`, `txpool_*`, `debug_*`
|
|
72
|
-
- Orbinum-specific namespace: `privacy_*`
|
|
73
|
-
|
|
74
|
-
In the SDK, `rpc-v2` is modeled as a dedicated top-level module, exposed through `client.rpcV2`. Each namespace lives under `src/rpc-v2/`.
|
|
75
|
-
|
|
76
|
-
Current typed coverage:
|
|
77
|
-
|
|
78
|
-
- `client.rpcV2.privacy.getMerkleRoot()`
|
|
79
|
-
- `client.rpcV2.privacy.getMerkleProof(leafIndex)`
|
|
80
|
-
- `client.rpcV2.privacy.getNullifierStatus(nullifier)`
|
|
81
|
-
- `client.rpcV2.privacy.getPoolStats()`
|
|
82
|
-
|
|
83
|
-
Example:
|
|
84
|
-
|
|
85
|
-
```ts
|
|
86
|
-
const root = await client.rpcV2.privacy.getMerkleRoot();
|
|
87
|
-
|
|
88
|
-
const proof = await client.rpcV2.privacy.getMerkleProof(12);
|
|
89
|
-
// { path: string[]; leafIndex: number; treeDepth: number; }
|
|
90
|
-
|
|
91
|
-
const status = await client.rpcV2.privacy.getNullifierStatus('0x...');
|
|
92
|
-
// { nullifier: string; isSpent: boolean; }
|
|
93
|
-
|
|
94
|
-
const stats = await client.rpcV2.privacy.getPoolStats();
|
|
95
|
-
// { merkleRoot: string; commitmentCount: number; totalBalance: string; ... }
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
Notes:
|
|
99
|
-
|
|
100
|
-
- `rpc-v2` responses use `snake_case` from the node; the SDK normalizes them to `camelCase`.
|
|
101
|
-
- `u128` values are exposed as decimal strings to avoid precision loss.
|
|
102
|
-
|
|
103
|
-
## Modules
|
|
104
|
-
|
|
105
|
-
### SubstrateClient
|
|
106
|
-
|
|
107
|
-
Thin wrapper over [polkadot-api](https://github.com/polkadot-api/polkadot-api) (PAPI) for Substrate WebSocket communication.
|
|
108
|
-
|
|
109
|
-
```ts
|
|
110
|
-
import { SubstrateClient } from '@orbinum/sdk';
|
|
111
|
-
|
|
112
|
-
const substrate = await SubstrateClient.connect('ws://localhost:9944');
|
|
113
|
-
|
|
114
|
-
const info = await substrate.getChainInfo();
|
|
115
|
-
const health = await substrate.getHealth();
|
|
116
|
-
const version = await substrate.getNodeVersion();
|
|
117
|
-
const genesis = await substrate.getGenesisHash();
|
|
118
|
-
|
|
119
|
-
// Block queries
|
|
120
|
-
const header = await substrate.getBlockHeader('best');
|
|
121
|
-
const hash = await substrate.getBlockHash(1000);
|
|
122
|
-
const block = await substrate.getBlock('0xabc...');
|
|
123
|
-
|
|
124
|
-
// Block events — decoded into EventRecord[]
|
|
125
|
-
const events = await substrate.queryBlockEvents('0xabc...');
|
|
126
|
-
|
|
127
|
-
// Block stream
|
|
128
|
-
substrate.blocks$.subscribe(block => console.log(block.number));
|
|
129
|
-
|
|
130
|
-
// Raw JSON-RPC call
|
|
131
|
-
const result = await substrate.request('system_name', []);
|
|
132
|
-
|
|
133
|
-
// Transaction helpers
|
|
134
|
-
const tx = await substrate.txFromCallData(callBytes);
|
|
135
|
-
const finalized = await tx.signAndSubmit(signer);
|
|
136
|
-
const finalized2 = await substrate.submit(signedHex);
|
|
137
|
-
substrate.submitAndWatch(signedHex).subscribe(event => console.log(event));
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
### EvmClient
|
|
141
|
-
|
|
142
|
-
Stateless HTTP client following the Ethereum JSON-RPC specification.
|
|
143
|
-
|
|
144
|
-
```ts
|
|
145
|
-
import { EvmClient } from '@orbinum/sdk';
|
|
146
|
-
|
|
147
|
-
const evm = new EvmClient('http://localhost:9933');
|
|
148
|
-
|
|
149
|
-
const balance = await evm.getBalance('0xYourAddress');
|
|
150
|
-
const chainId = await evm.getChainId();
|
|
151
|
-
const txHash = await evm.sendRawTransaction(signedHex);
|
|
152
|
-
|
|
153
|
-
// Batch multiple calls in a single HTTP request
|
|
154
|
-
const [bal, nonce] = await evm.batchRequest([
|
|
155
|
-
{ method: 'eth_getBalance', params: ['0xAddr', 'latest'] },
|
|
156
|
-
{ method: 'eth_getTransactionCount', params: ['0xAddr', 'latest'] },
|
|
157
|
-
]);
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
### EvmExplorer
|
|
161
|
-
|
|
162
|
-
Read-only EVM block and transaction explorer.
|
|
163
|
-
|
|
164
|
-
```ts
|
|
165
|
-
import { EvmExplorer } from '@orbinum/sdk';
|
|
166
|
-
|
|
167
|
-
const explorer = new EvmExplorer('http://localhost:9933');
|
|
168
|
-
|
|
169
|
-
const block = await explorer.getBlock(12345);
|
|
170
|
-
const tx = await explorer.getTransaction('0xhash...');
|
|
171
|
-
const logs = await explorer.getLogs({ fromBlock: 100, toBlock: 200, address: '0x...' });
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
Types: `EvmBlock`, `EvmTransaction`, `EvmAddressInfo`, `EvmTxSummary`, `EvmLog`, `TokenInfo`, `TokenTransfer`.
|
|
175
|
-
|
|
176
|
-
### ZkVerifierModule
|
|
177
|
-
|
|
178
|
-
Typed access to the on-chain ZK verifier — circuit version info, VK hashes, and version history.
|
|
179
|
-
|
|
180
|
-
```ts
|
|
181
|
-
import { ZkVerifierModule } from '@orbinum/sdk';
|
|
182
|
-
|
|
183
|
-
const zkv = new ZkVerifierModule(substrate);
|
|
184
|
-
|
|
185
|
-
const info = await zkv.getCircuitVersionInfo('circuit-id');
|
|
186
|
-
const vkHash = await zkv.getVkHash('circuit-id', 1);
|
|
187
|
-
const stats = await zkv.getVersionStats('circuit-id');
|
|
188
|
-
const history = await zkv.getHistoricalVersions('circuit-id');
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
### ShieldedPoolModule
|
|
192
|
-
|
|
193
|
-
High-level interface for shielded pool extrinsics and note workflows. Requires a loaded `PrivacyKeyManager` and ZK proofs for unshield and private transfer.
|
|
194
|
-
|
|
195
|
-
```ts
|
|
196
|
-
// Shield (deposit) tokens into the shielded pool
|
|
197
|
-
const { txResult, note } = await client.shieldedPool.buildAndShield({
|
|
198
|
-
amount: 1_000_000n,
|
|
199
|
-
assetId: 1,
|
|
200
|
-
tokenAddress: '0xTokenAddress',
|
|
201
|
-
}, signer);
|
|
202
|
-
|
|
203
|
-
// Private transfer between notes
|
|
204
|
-
await client.shieldedPool.privateTransfer({ ...params }, signer);
|
|
205
|
-
|
|
206
|
-
// Unshield (withdraw) tokens to a public address
|
|
207
|
-
await client.shieldedPool.unshield({ ...params }, signer);
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
### PrivacyModule (`rpc-v2`)
|
|
211
|
-
|
|
212
|
-
Typed wrapper for Orbinum `privacy_*` endpoints from `rpc-v2`.
|
|
213
|
-
|
|
214
|
-
```ts
|
|
215
|
-
import { PrivacyModule } from '@orbinum/sdk';
|
|
216
|
-
|
|
217
|
-
const privacy = new PrivacyModule(substrate);
|
|
218
|
-
|
|
219
|
-
const merkleRoot = await privacy.getMerkleRoot();
|
|
220
|
-
const merkleProof = await privacy.getMerkleProof(4);
|
|
221
|
-
const nullifier = await privacy.getNullifierStatus('0xNullifier');
|
|
222
|
-
const stats = await privacy.getPoolStats();
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
### Privacy Keys
|
|
226
|
-
|
|
227
|
-
Key derivation follows a deterministic chain from the user's wallet signature. No key material is ever stored by the SDK.
|
|
228
|
-
|
|
229
|
-
```ts
|
|
230
|
-
import {
|
|
231
|
-
PrivacyKeyManager,
|
|
232
|
-
deriveOwnerPk,
|
|
233
|
-
deriveSpendingKeyFromSignature,
|
|
234
|
-
deriveSpendingKeyMessage,
|
|
235
|
-
deriveViewingKey,
|
|
236
|
-
} from '@orbinum/sdk';
|
|
237
|
-
|
|
238
|
-
const message = deriveSpendingKeyMessage(chainId, evmAddress);
|
|
239
|
-
const spendingKey = deriveSpendingKeyFromSignature(sigHex, chainId, evmAddress);
|
|
240
|
-
|
|
241
|
-
const keyManager = new PrivacyKeyManager();
|
|
242
|
-
keyManager.load(spendingKey);
|
|
243
|
-
|
|
244
|
-
const viewingKey = deriveViewingKey(spendingKey);
|
|
245
|
-
const ownerPk = deriveOwnerPk(spendingKey);
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
### AccountMappingModule
|
|
249
|
-
|
|
250
|
-
Manage on-chain identity by linking Substrate and EVM accounts, registering aliases, and interacting with the alias marketplace.
|
|
251
|
-
|
|
252
|
-
```ts
|
|
253
|
-
await client.accountMapping.registerAlias({ alias: 'myalias' }, signer);
|
|
254
|
-
await client.accountMapping.addChainLink({ chainId: 1, address: '0xEvmAddr' }, signer);
|
|
255
|
-
|
|
256
|
-
const alias = await client.accountMapping.getAliasOf(substrateHex);
|
|
257
|
-
const linked = await client.accountMapping.getChainLinks(substrateHex);
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
### IndexerClient
|
|
261
|
-
|
|
262
|
-
HTTP client for the Orbinum indexer REST API. All list endpoints are paginated.
|
|
263
|
-
|
|
264
|
-
```ts
|
|
265
|
-
import { IndexerClient } from '@orbinum/sdk';
|
|
266
|
-
|
|
267
|
-
const indexer = new IndexerClient({ baseUrl: 'https://indexer.orbinum.io' });
|
|
268
|
-
|
|
269
|
-
const blocks = await indexer.getBlocks({ page: 1, limit: 20 });
|
|
270
|
-
const extrinsics = await indexer.getExtrinsics({ address: '5F...' });
|
|
271
|
-
const commitments = await indexer.getCommitments({ page: 1, limit: 20 });
|
|
272
|
-
const nullifier = await indexer.getNullifierStatus('0xNullifier');
|
|
273
|
-
const roots = await indexer.getMerkleRoots({ limit: 10 });
|
|
274
|
-
const evmTxs = await indexer.getEvmTransactions({ address: '0xabc...' });
|
|
275
|
-
const stats = await indexer.getStats();
|
|
276
|
-
```
|
|
277
|
-
|
|
278
|
-
### Precompiles
|
|
279
|
-
|
|
280
|
-
Typed wrappers for Orbinum's EVM precompile contracts.
|
|
281
|
-
|
|
282
|
-
| Precompile | Address | Description |
|
|
283
|
-
|---|---|---|
|
|
284
|
-
| `ShieldedPoolPrecompile` | `0x0801` | Shield, unshield, and private transfer from EVM |
|
|
285
|
-
| `AccountMappingPrecompile` | `0x0800` | Identity and alias operations from EVM |
|
|
286
|
-
| `CryptoPrecompiles` | `0x0400–0x0403` | Frontier crypto utilities |
|
|
287
|
-
|
|
288
|
-
```ts
|
|
289
|
-
import { ShieldedPoolPrecompile, decodePrecompileCalldata } from '@orbinum/sdk';
|
|
290
|
-
|
|
291
|
-
const precompile = new ShieldedPoolPrecompile(evmClient);
|
|
292
|
-
await precompile.shield(amount, tokenAddress, commitment, walletSigner);
|
|
293
|
-
|
|
294
|
-
// Decode raw EVM calldata for a known precompile
|
|
295
|
-
const decoded = decodePrecompileCalldata('0x0800', calldata);
|
|
296
|
-
```
|
|
297
|
-
|
|
298
|
-
## Extrinsic & Event Decoders
|
|
299
|
-
|
|
300
|
-
The `mapExtrinsicArgs` and `mapZkEventData` helpers decode raw pallet call data from the indexer or block scanner into typed objects.
|
|
301
|
-
|
|
302
|
-
```ts
|
|
303
|
-
import { mapExtrinsicArgs, mapZkEventData } from '@orbinum/sdk';
|
|
304
|
-
|
|
305
|
-
const decoded = mapExtrinsicArgs('shieldedPool', 'shield', rawArgs);
|
|
306
|
-
// DecodedShieldArgs | DecodedUnshieldArgs | ...
|
|
307
|
-
|
|
308
|
-
const eventData = mapZkEventData('ProofVerified', rawEventData);
|
|
309
|
-
```
|
|
310
|
-
|
|
311
|
-
## Substrate SCALE Primitives
|
|
312
|
-
|
|
313
|
-
SCALE codec primitives from `@polkadot-api/substrate-bindings` are re-exported directly from the SDK. There is no need to install that package separately.
|
|
314
|
-
|
|
315
|
-
```ts
|
|
316
|
-
import { Blake2256, AccountId, u128, u64, Storage, Keccak256 } from '@orbinum/sdk';
|
|
317
|
-
import { base58, getSs58AddressInfo } from '@orbinum/sdk';
|
|
318
|
-
```
|
|
319
|
-
|
|
320
|
-
## Key Derivation Chain
|
|
321
|
-
|
|
322
|
-
```
|
|
323
|
-
wallet.sign(message)
|
|
324
|
-
|
|
|
325
|
-
v
|
|
326
|
-
deriveSpendingKeyFromSignature() -- HKDF-SHA256 over signature bytes, mod BN254_R
|
|
327
|
-
|
|
|
328
|
-
+-- deriveViewingKey() -- HKDF-SHA256 -> 32-byte ChaCha20 symmetric key
|
|
329
|
-
| (used to encrypt/decrypt note memos)
|
|
330
|
-
|
|
|
331
|
-
+-- deriveOwnerPk() -- BabyJubJub scalar multiplication -> Ax
|
|
332
|
-
(embedded in shielded note commitments)
|
|
333
|
-
```
|
|
334
|
-
|
|
335
|
-
## Development
|
|
336
|
-
|
|
337
|
-
### Typing Layout
|
|
338
|
-
|
|
339
|
-
The SDK organizes types by feature ownership.
|
|
340
|
-
|
|
341
|
-
- `types/index.ts`: public shared types of the feature
|
|
342
|
-
- `types/pallet-events.ts`: event payloads and discriminated unions for that pallet
|
|
343
|
-
- `types/pallet-extrinsics.ts`: extrinsic argument types and call unions for that pallet
|
|
344
|
-
- `types/raw.ts`: internal node or RPC response shapes used only for transport mapping
|
|
345
|
-
|
|
346
|
-
Examples:
|
|
347
|
-
|
|
348
|
-
- `src/shielded-pool/types/index.ts`
|
|
349
|
-
- `src/shielded-pool/types/pallet-events.ts`
|
|
350
|
-
- `src/shielded-pool/types/pallet-extrinsics.ts`
|
|
351
|
-
- `src/rpc-v2/types/raw.ts`
|
|
352
|
-
- `src/indexer/types/index.ts`
|
|
353
|
-
- `src/account-mapping/types/index.ts`
|
|
354
|
-
|
|
355
|
-
Rules:
|
|
356
|
-
|
|
357
|
-
1. If a type is part of the public API of a feature, keep it in that feature's `types/index.ts`.
|
|
358
|
-
2. If a type only mirrors a transport payload from RPC, keep it in `types/raw.ts`.
|
|
359
|
-
3. Do not reintroduce a global `src/types.ts` or `src/types/` directory.
|
|
360
|
-
4. Export public types from `src/index.ts`, but keep ownership at the feature level.
|
|
361
|
-
|
|
362
|
-
### Extending `rpc-v2`
|
|
363
|
-
|
|
364
|
-
The intended extension pattern is namespace-oriented and centralized under `RpcV2Module`.
|
|
365
|
-
|
|
366
|
-
For example, to add `net_*` support:
|
|
367
|
-
|
|
368
|
-
1. Create `src/rpc-v2/NetModule.ts`
|
|
369
|
-
2. Define raw response types that match the node's JSON exactly
|
|
370
|
-
3. Map raw `snake_case` or hex-shaped values into stable SDK-facing TypeScript types
|
|
371
|
-
4. Export the module from `src/rpc-v2/index.ts`
|
|
372
|
-
5. Attach it in `RpcV2Module`, for example as `client.rpcV2.net`
|
|
373
|
-
6. Add unit tests that verify RPC method names, params, and response mapping
|
|
374
|
-
|
|
375
|
-
```ts
|
|
376
|
-
export class NetModule {
|
|
377
|
-
constructor(private readonly substrate: SubstrateClient) {}
|
|
378
|
-
|
|
379
|
-
async version(): Promise<string> {
|
|
380
|
-
return this.substrate.request<string>('net_version', []);
|
|
381
|
-
}
|
|
382
|
-
}
|
|
28
|
+
const info = await client.substrate.getChainInfo();
|
|
29
|
+
const root = await client.rpcV2.privacy.getMerkleRoot();
|
|
30
|
+
const block = await client.substrate.getBlock('best');
|
|
383
31
|
```
|
|
384
32
|
|
|
385
|
-
|
|
33
|
+
`OrbinumClient` exposes the following modules:
|
|
386
34
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
35
|
+
| Property | Description |
|
|
36
|
+
|---|---|
|
|
37
|
+
| `client.substrate` | Substrate RPC — blocks, events, transactions |
|
|
38
|
+
| `client.evm` | EVM JSON-RPC client (`null` if `evmRpc` not set) |
|
|
39
|
+
| `client.rpcV2` | Orbinum `rpc-v2` namespaces (`privacy_*`, etc.) |
|
|
40
|
+
| `client.shieldedPool` | Shielded pool extrinsics |
|
|
41
|
+
| `client.accountMapping` | Alias, chain-link, and identity operations |
|
|
42
|
+
| `client.precompiles` | EVM precompile wrappers (`null` if `evmRpc` not set) |
|
|
395
43
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
ISC
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
## Installation
|
|
402
|
-
|
|
403
|
-
```bash
|
|
404
|
-
npm install @orbinum/sdk
|
|
405
|
-
# or
|
|
406
|
-
pnpm add @orbinum/sdk
|
|
407
|
-
```
|
|
408
|
-
|
|
409
|
-
## Requirements
|
|
410
|
-
|
|
411
|
-
- Node.js 18 or later
|
|
412
|
-
- A running Orbinum node (Substrate WebSocket endpoint)
|
|
413
|
-
- An EVM JSON-RPC endpoint (optional, required for EVM and precompile operations)
|
|
414
|
-
|
|
415
|
-
## Quick Start
|
|
416
|
-
|
|
417
|
-
```ts
|
|
418
|
-
import { OrbinumClient } from '@orbinum/sdk';
|
|
419
|
-
|
|
420
|
-
const client = await OrbinumClient.connect({
|
|
421
|
-
substrateWs: 'ws://localhost:9944',
|
|
422
|
-
evmRpc: 'http://localhost:9933',
|
|
423
|
-
});
|
|
424
|
-
|
|
425
|
-
const chainInfo = await client.substrate.getChainInfo();
|
|
426
|
-
const root = await client.rpcV2.privacy.getMerkleRoot();
|
|
427
|
-
const tree = await client.shieldedPool.merkle.getTreeInfo();
|
|
428
|
-
```
|
|
429
|
-
|
|
430
|
-
`OrbinumClient` wires together the current SDK surface under a single interface.
|
|
431
|
-
|
|
432
|
-
- `client.substrate`: Substrate WebSocket RPC and transaction helpers
|
|
433
|
-
- `client.evm`: EVM JSON-RPC client, or `null` if `evmRpc` was not configured
|
|
434
|
-
- `client.rpcV2`: typed Orbinum `rpc-v2` namespaces
|
|
435
|
-
- `client.shieldedPool`: shielded-pool extrinsics, note helpers, and Merkle queries
|
|
436
|
-
- `client.accountMapping`: alias, chain-link, metadata, and marketplace operations
|
|
437
|
-
- `client.precompiles`: EVM precompile wrappers, or `null` if `evmRpc` was not configured
|
|
438
|
-
|
|
439
|
-
Each module can also be instantiated independently.
|
|
440
|
-
|
|
441
|
-
## rpc-v2
|
|
442
|
-
|
|
443
|
-
Orbinum's `rpc-v2` is organized by namespace.
|
|
444
|
-
|
|
445
|
-
- Standard Frontier namespaces: `eth_*`, `net_*`, `web3_*`, `txpool_*`, `debug_*`
|
|
446
|
-
- Orbinum-specific namespace: `privacy_*`
|
|
447
|
-
|
|
448
|
-
In the SDK, `rpc-v2` is modeled as a dedicated top-level module, exposed through `client.rpcV2`. Each namespace lives under `src/rpc-v2/` and is grouped there instead of being mixed into legacy protocol modules.
|
|
449
|
-
|
|
450
|
-
Current typed coverage:
|
|
451
|
-
|
|
452
|
-
- `client.rpcV2.privacy.getMerkleRoot()`
|
|
453
|
-
- `client.rpcV2.privacy.getMerkleProof(leafIndex)`
|
|
454
|
-
- `client.rpcV2.privacy.getNullifierStatus(nullifier)`
|
|
455
|
-
- `client.rpcV2.privacy.getPoolStats()`
|
|
456
|
-
|
|
457
|
-
Example:
|
|
458
|
-
|
|
459
|
-
```ts
|
|
460
|
-
const root = await client.rpcV2.privacy.getMerkleRoot();
|
|
461
|
-
|
|
462
|
-
const proof = await client.rpcV2.privacy.getMerkleProof(12);
|
|
463
|
-
// {
|
|
464
|
-
// path: string[];
|
|
465
|
-
// leafIndex: number;
|
|
466
|
-
// treeDepth: number;
|
|
467
|
-
// }
|
|
468
|
-
|
|
469
|
-
const status = await client.rpcV2.privacy.getNullifierStatus('0x...');
|
|
470
|
-
// {
|
|
471
|
-
// nullifier: string;
|
|
472
|
-
// isSpent: boolean;
|
|
473
|
-
// }
|
|
474
|
-
|
|
475
|
-
const stats = await client.rpcV2.privacy.getPoolStats();
|
|
476
|
-
// {
|
|
477
|
-
// merkleRoot: string;
|
|
478
|
-
// commitmentCount: number;
|
|
479
|
-
// totalBalance: string;
|
|
480
|
-
// assetBalances: { assetId: number; balance: string }[];
|
|
481
|
-
// treeDepth: number;
|
|
482
|
-
// }
|
|
483
|
-
```
|
|
484
|
-
|
|
485
|
-
Notes:
|
|
486
|
-
|
|
487
|
-
- `rpc-v2` responses from the node use `snake_case`; the SDK maps them to `camelCase`.
|
|
488
|
-
- `u128` values are exposed as decimal strings to avoid precision loss in JavaScript.
|
|
489
|
-
- Privacy queries belong to `client.rpcV2.privacy.*`; they are no longer described as part of `ShieldedPoolModule`.
|
|
490
|
-
|
|
491
|
-
## Modules
|
|
492
|
-
|
|
493
|
-
### SubstrateClient
|
|
494
|
-
|
|
495
|
-
Thin wrapper over [polkadot-api](https://github.com/polkadot-api/polkadot-api) (PAPI) for Substrate WebSocket communication.
|
|
496
|
-
|
|
497
|
-
```ts
|
|
498
|
-
import { SubstrateClient } from '@orbinum/sdk';
|
|
499
|
-
|
|
500
|
-
const substrate = await SubstrateClient.connect('ws://localhost:9944');
|
|
501
|
-
|
|
502
|
-
const info = await substrate.getChainInfo();
|
|
503
|
-
const health = await substrate.getHealth();
|
|
504
|
-
const version = await substrate.getNodeVersion();
|
|
505
|
-
const genesis = await substrate.getGenesisHash();
|
|
506
|
-
|
|
507
|
-
// Raw JSON-RPC call
|
|
508
|
-
const result = await substrate.request('system_name', []);
|
|
509
|
-
|
|
510
|
-
// Build a transaction from pre-encoded SCALE call bytes
|
|
511
|
-
const tx = await substrate.txFromCallData(callBytes);
|
|
512
|
-
const finalized = await tx.signAndSubmit(signer);
|
|
513
|
-
|
|
514
|
-
// Submit a pre-signed extrinsic
|
|
515
|
-
const finalized = await substrate.submit(signedHex);
|
|
516
|
-
|
|
517
|
-
// Submit and observe lifecycle events
|
|
518
|
-
substrate.submitAndWatch(signedHex).subscribe(event => console.log(event));
|
|
519
|
-
```
|
|
520
|
-
|
|
521
|
-
### EvmClient
|
|
522
|
-
|
|
523
|
-
Stateless HTTP client following the Ethereum JSON-RPC specification.
|
|
524
|
-
|
|
525
|
-
```ts
|
|
526
|
-
import { EvmClient } from '@orbinum/sdk';
|
|
527
|
-
|
|
528
|
-
const evm = new EvmClient('http://localhost:9933');
|
|
529
|
-
|
|
530
|
-
const balance = await evm.getBalance('0xYourAddress');
|
|
531
|
-
const chainId = await evm.getChainId();
|
|
532
|
-
const txHash = await evm.sendRawTransaction(signedHex);
|
|
533
|
-
|
|
534
|
-
// Batch multiple calls in a single HTTP request
|
|
535
|
-
const [balance, nonce] = await evm.batchRequest([
|
|
536
|
-
{ method: 'eth_getBalance', params: ['0xAddr', 'latest'] },
|
|
537
|
-
{ method: 'eth_getTransactionCount', params: ['0xAddr', 'latest'] },
|
|
538
|
-
]);
|
|
539
|
-
```
|
|
540
|
-
|
|
541
|
-
### RpcV2Module
|
|
542
|
-
|
|
543
|
-
Top-level typed entry point for Orbinum `rpc-v2` namespaces.
|
|
544
|
-
|
|
545
|
-
```ts
|
|
546
|
-
const root = await client.rpcV2.privacy.getMerkleRoot();
|
|
547
|
-
const proof = await client.rpcV2.privacy.getMerkleProof(4);
|
|
548
|
-
const nullifier = await client.rpcV2.privacy.getNullifierStatus('0xNullifier');
|
|
549
|
-
const stats = await client.rpcV2.privacy.getPoolStats();
|
|
550
|
-
```
|
|
551
|
-
|
|
552
|
-
### ShieldedPoolModule
|
|
553
|
-
|
|
554
|
-
High-level interface for shielded pool extrinsics and note workflows. Requires a loaded `PrivacyKeyManager` and ZK proofs for unshield and private transfer.
|
|
555
|
-
|
|
556
|
-
```ts
|
|
557
|
-
// Shield (deposit) tokens into the shielded pool
|
|
558
|
-
const { txResult, note } = await client.shieldedPool.buildAndShield({
|
|
559
|
-
amount: 1_000_000n,
|
|
560
|
-
assetId: 1,
|
|
561
|
-
tokenAddress: '0xTokenAddress',
|
|
562
|
-
}, signer);
|
|
563
|
-
|
|
564
|
-
const treeInfo = await client.shieldedPool.merkle.getTreeInfo();
|
|
565
|
-
|
|
566
|
-
// Private transfer between notes
|
|
567
|
-
await client.shieldedPool.privateTransfer({ ...params }, signer);
|
|
568
|
-
|
|
569
|
-
// Unshield (withdraw) tokens to a public address
|
|
570
|
-
await client.shieldedPool.unshield({ ...params }, signer);
|
|
571
|
-
|
|
572
|
-
```
|
|
573
|
-
|
|
574
|
-
### PrivacyModule (`rpc-v2`)
|
|
575
|
-
|
|
576
|
-
Typed wrapper for Orbinum `privacy_*` endpoints from `rpc-v2`.
|
|
577
|
-
|
|
578
|
-
```ts
|
|
579
|
-
import { PrivacyModule } from '@orbinum/sdk';
|
|
580
|
-
|
|
581
|
-
const privacy = new PrivacyModule(client.substrate);
|
|
582
|
-
|
|
583
|
-
const merkleRoot = await privacy.getMerkleRoot();
|
|
584
|
-
const merkleProof = await privacy.getMerkleProof(4);
|
|
585
|
-
const nullifier = await privacy.getNullifierStatus('0xNullifier');
|
|
586
|
-
const stats = await privacy.getPoolStats();
|
|
587
|
-
```
|
|
588
|
-
|
|
589
|
-
### Privacy Keys
|
|
590
|
-
|
|
591
|
-
Key derivation follows a deterministic chain from the user's wallet signature. No key material is ever stored by the SDK.
|
|
592
|
-
|
|
593
|
-
```ts
|
|
594
|
-
import {
|
|
595
|
-
PrivacyKeyManager,
|
|
596
|
-
deriveOwnerPk,
|
|
597
|
-
deriveSpendingKeyFromSignature,
|
|
598
|
-
deriveSpendingKeyMessage,
|
|
599
|
-
deriveViewingKey,
|
|
600
|
-
} from '@orbinum/sdk';
|
|
601
|
-
|
|
602
|
-
// 1. Get the message the user must sign
|
|
603
|
-
const message = deriveSpendingKeyMessage(chainId, evmAddress);
|
|
604
|
-
|
|
605
|
-
// 2. Derive the spending key from the wallet signature
|
|
606
|
-
const spendingKey = deriveSpendingKeyFromSignature(sigHex, chainId, evmAddress);
|
|
607
|
-
|
|
608
|
-
// 3. Load into the key manager
|
|
609
|
-
const keyManager = new PrivacyKeyManager();
|
|
610
|
-
keyManager.load(spendingKey);
|
|
611
|
-
|
|
612
|
-
// The manager derives the viewing key (ChaCha20 memo decryption)
|
|
613
|
-
// and owner public key (BabyJubJub, used in note commitments)
|
|
614
|
-
const viewingKey = deriveViewingKey(spendingKey);
|
|
615
|
-
const ownerPk = deriveOwnerPk(spendingKey);
|
|
616
|
-
```
|
|
617
|
-
|
|
618
|
-
### AccountMappingModule
|
|
619
|
-
|
|
620
|
-
Manage on-chain identity by linking Substrate and EVM accounts, registering aliases, and interacting with the alias marketplace.
|
|
621
|
-
|
|
622
|
-
```ts
|
|
623
|
-
// Register an alias
|
|
624
|
-
await client.accountMapping.registerAlias({ alias: 'myalias' }, signer);
|
|
625
|
-
|
|
626
|
-
// Link an EVM address to a Substrate account
|
|
627
|
-
await client.accountMapping.addChainLink({ chainId: 1, address: '0xEvmAddr' }, signer);
|
|
628
|
-
|
|
629
|
-
// Query
|
|
630
|
-
const alias = await client.accountMapping.getAliasOf(substrateHex);
|
|
631
|
-
const linked = await client.accountMapping.getChainLinks(substrateHex);
|
|
632
|
-
```
|
|
633
|
-
|
|
634
|
-
### IndexerClient
|
|
635
|
-
|
|
636
|
-
HTTP client for the Orbinum indexer REST API. The indexer runs as a separate service indexed from node events.
|
|
637
|
-
|
|
638
|
-
```ts
|
|
639
|
-
import { IndexerClient } from '@orbinum/sdk';
|
|
640
|
-
|
|
641
|
-
const indexer = new IndexerClient({
|
|
642
|
-
baseUrl: 'https://indexer.orbinum.io',
|
|
643
|
-
});
|
|
644
|
-
|
|
645
|
-
const commitments = await indexer.getCommitments({ page: 1, limit: 20 });
|
|
646
|
-
const status = await indexer.getNullifierStatus('0xNullifier');
|
|
647
|
-
const roots = await indexer.getMerkleRoots({ limit: 10 });
|
|
648
|
-
const extrinsics = await indexer.getAddressExtrinsics('5F...');
|
|
649
|
-
const evmTxs = await indexer.getEvmTransactions({ address: '0xabc...' });
|
|
650
|
-
```
|
|
651
|
-
|
|
652
|
-
### Precompiles
|
|
653
|
-
|
|
654
|
-
Typed wrappers for Orbinum's EVM precompile contracts, callable from any EVM wallet or signer.
|
|
655
|
-
|
|
656
|
-
| Precompile | Address | Description |
|
|
657
|
-
|---|---|---|
|
|
658
|
-
| `ShieldedPoolPrecompile` | `0x0801` | Shield, unshield, and private transfer from EVM |
|
|
659
|
-
| `AccountMappingPrecompile` | `0x0800` | Identity and alias operations from EVM |
|
|
660
|
-
| `CryptoPrecompiles` | `0x0400–0x0403` | Frontier crypto utilities (SHA3, EC recover, Curve25519) |
|
|
661
|
-
|
|
662
|
-
```ts
|
|
663
|
-
import { ShieldedPoolPrecompile } from '@orbinum/sdk';
|
|
664
|
-
|
|
665
|
-
const precompile = new ShieldedPoolPrecompile(evmClient);
|
|
666
|
-
await precompile.shield(amount, tokenAddress, commitment, walletSigner);
|
|
667
|
-
```
|
|
668
|
-
|
|
669
|
-
## Key Derivation Chain
|
|
670
|
-
|
|
671
|
-
```
|
|
672
|
-
wallet.sign(message)
|
|
673
|
-
|
|
|
674
|
-
v
|
|
675
|
-
deriveSpendingKeyFromSignature() -- HKDF-SHA256 over signature bytes, mod BN254_R
|
|
676
|
-
|
|
|
677
|
-
+-- deriveViewingKey() -- HKDF-SHA256 -> 32-byte ChaCha20 symmetric key
|
|
678
|
-
| (used to encrypt/decrypt note memos)
|
|
679
|
-
|
|
|
680
|
-
+-- deriveOwnerPk() -- BabyJubJub scalar multiplication -> Ax
|
|
681
|
-
(embedded in shielded note commitments)
|
|
682
|
-
```
|
|
683
|
-
|
|
684
|
-
## Development
|
|
685
|
-
|
|
686
|
-
### Typing Layout
|
|
687
|
-
|
|
688
|
-
The SDK organizes types by feature ownership.
|
|
689
|
-
|
|
690
|
-
- `types/index.ts`: public shared types of the feature
|
|
691
|
-
- `types/pallet-events.ts`: event payloads and discriminated unions for that pallet
|
|
692
|
-
- `types/pallet-extrinsics.ts`: extrinsic argument types and call unions for that pallet
|
|
693
|
-
- `types/raw.ts`: internal node or RPC response shapes used only for transport mapping
|
|
694
|
-
|
|
695
|
-
Examples:
|
|
696
|
-
|
|
697
|
-
- `src/shielded-pool/types/index.ts`
|
|
698
|
-
- `src/shielded-pool/types/pallet-events.ts`
|
|
699
|
-
- `src/shielded-pool/types/pallet-extrinsics.ts`
|
|
700
|
-
- `src/rpc-v2/types/raw.ts`
|
|
701
|
-
- `src/indexer/types/index.ts`
|
|
702
|
-
- `src/precompiles/types/index.ts`
|
|
703
|
-
|
|
704
|
-
Rules:
|
|
705
|
-
|
|
706
|
-
1. If a type is part of the public API of a feature, keep it in that feature's `types/index.ts`.
|
|
707
|
-
2. If a type only mirrors a transport payload from RPC, keep it in `types/raw.ts`.
|
|
708
|
-
3. Do not reintroduce a global `src/types.ts` or `src/types/` directory.
|
|
709
|
-
4. Export public types from `src/index.ts`, but keep ownership at the feature level.
|
|
710
|
-
|
|
711
|
-
### Extending `rpc-v2`
|
|
712
|
-
|
|
713
|
-
The intended extension pattern is namespace-oriented and centralized under `RpcV2Module`.
|
|
714
|
-
|
|
715
|
-
For example, to add `net_*` support:
|
|
716
|
-
|
|
717
|
-
1. Create `src/rpc-v2/NetModule.ts`
|
|
718
|
-
2. Define raw response types that match the node's JSON exactly
|
|
719
|
-
3. Map raw `snake_case` or hex-shaped values into stable SDK-facing TypeScript types
|
|
720
|
-
4. Export the module from `src/rpc-v2/index.ts`
|
|
721
|
-
5. Attach it in `RpcV2Module`, for example as `client.rpcV2.net`
|
|
722
|
-
6. Add unit tests that verify RPC method names, params, and response mapping
|
|
723
|
-
|
|
724
|
-
Minimal shape example:
|
|
725
|
-
|
|
726
|
-
```ts
|
|
727
|
-
export class NetModule {
|
|
728
|
-
constructor(private readonly substrate: SubstrateClient) {}
|
|
729
|
-
|
|
730
|
-
async version(): Promise<string> {
|
|
731
|
-
return this.substrate.request<string>('net_version', []);
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
async peerCount(): Promise<string> {
|
|
735
|
-
return this.substrate.request<string>('net_peerCount', []);
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
async listening(): Promise<boolean> {
|
|
739
|
-
return this.substrate.request<boolean>('net_listening', []);
|
|
740
|
-
}
|
|
741
|
-
}
|
|
742
|
-
```
|
|
743
|
-
|
|
744
|
-
The same approach applies to `web3_*`, `txpool_*`, `eth_*`, and any future Orbinum-specific namespaces.
|
|
745
|
-
|
|
746
|
-
```bash
|
|
747
|
-
pnpm install
|
|
748
|
-
pnpm build # compile to dist/ (ESM + CJS + types)
|
|
749
|
-
pnpm test # run test suite
|
|
750
|
-
pnpm typecheck:all # typecheck src and tests
|
|
751
|
-
pnpm lint # eslint
|
|
752
|
-
pnpm format # prettier
|
|
753
|
-
```
|
|
44
|
+
Each module can also be instantiated independently without `OrbinumClient`.
|
|
754
45
|
|
|
755
46
|
## License
|
|
756
47
|
|
|
757
|
-
|
|
48
|
+
MIT
|