stellar-contracts-kit 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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Fajrin Firmana
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,434 @@
1
+ # stellar-contracts-kit
2
+
3
+ [![npm](https://img.shields.io/npm/v/stellar-contracts-kit)](https://www.npmjs.com/package/stellar-contracts-kit)
4
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
5
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue)](https://www.typescriptlang.org/)
6
+
7
+ TypeScript SDK for Soroban smart contracts on Stellar. Handles wallet connection, typed contract clients, and a CLI that generates TypeScript interfaces directly from any live on-chain contract.
8
+
9
+ ---
10
+
11
+ ## Features
12
+
13
+ - **CLI code generator**: `npx sck` generates fully-typed TypeScript or JavaScript interfaces from any live contract spec
14
+ - **Interactive CLI**: arrow-key command picker, input validation, network selection
15
+ - **Built-in wallet modal**: auto-detects Freighter, Cyphras, and Lobstr, opens a picker UI when no wallet is specified
16
+ - **Three call modes**: auto, read-only, and force-invoke on every contract method
17
+ - **Full type coverage**: structs, enums, unions, tuples, `Uint8Array`, `bigint`, `Option`, `Vec`, `Map`
18
+ - **Contract spec caching**: spec fetched once per contract ID, subsequent calls are instant
19
+ - **Contract restore**: one-call recovery when a contract's on-chain state has expired
20
+ - Works with any frontend framework (React, Vue, Svelte, vanilla JS)
21
+
22
+ ---
23
+
24
+ ## Installation
25
+
26
+ ```bash
27
+ npm install stellar-contracts-kit
28
+ ```
29
+
30
+ ---
31
+
32
+ ## CLI
33
+
34
+ ### Interactive mode
35
+
36
+ ```bash
37
+ npx sck
38
+ ```
39
+
40
+ Launches an arrow-key menu to pick a command, then guides you through the rest.
41
+
42
+ ### Generate types
43
+
44
+ Fetch a live contract spec and generate a typed TypeScript interface + example file:
45
+
46
+ ```bash
47
+ npx sck generate --contract CABC... --network testnet
48
+ ```
49
+
50
+ Or add it as an npm script so the team can run it without `npx`:
51
+
52
+ ```json
53
+ "scripts": {
54
+ "generate": "sck generate --contract CABC... --network testnet"
55
+ }
56
+ ```
57
+
58
+ ```bash
59
+ npm run generate
60
+ ```
61
+
62
+ With options:
63
+
64
+ ```bash
65
+ npx sck generate \
66
+ --contract CABC... \
67
+ --network testnet \
68
+ --name CounterContract \
69
+ --out src/contracts/counter.ts
70
+ ```
71
+
72
+ For JavaScript output:
73
+
74
+ ```bash
75
+ npx sck generate --contract CABC... --network testnet --js
76
+ ```
77
+
78
+ **Output files:**
79
+
80
+ | File | Description |
81
+ |------|-------------|
82
+ | `contracts/counter.ts` | TypeScript interface + custom types + contract ID |
83
+ | `contracts/counter.example.ts` | Ready-to-adapt usage example for every method |
84
+
85
+ **Generated `contracts/counter.ts`:**
86
+
87
+ ```ts
88
+ import type { ContractMethodFn } from 'stellar-contracts-kit'
89
+
90
+ export interface CounterContract {
91
+ get: ContractMethodFn<number, []>
92
+ increment: ContractMethodFn<number, []>
93
+ reset: ContractMethodFn<void, []>
94
+ }
95
+
96
+ export const CONTRACT_ID = 'CABC...' as const
97
+ ```
98
+
99
+ Re-run `npx sck generate` after a contract upgrade to refresh the types.
100
+
101
+ ### Inspect a contract
102
+
103
+ Print all functions and custom types for any contract directly in the terminal, without generating files:
104
+
105
+ ```bash
106
+ npx sck inspect --contract CABC... --network testnet
107
+ ```
108
+
109
+ **Output:**
110
+
111
+ ```
112
+ Contract : CABC...
113
+ Network : testnet
114
+
115
+ Functions (5)
116
+ get() -> number
117
+ increment() -> number
118
+ reset() -> void
119
+ transfer(from: string, to: string, amount: bigint) -> void
120
+ balance(account: string) -> bigint
121
+
122
+ Custom Types (2)
123
+ struct TokenInfo { symbol: string, decimals: number }
124
+ enum Status { Active = 0, Inactive = 1 }
125
+ ```
126
+
127
+ ### Custom network
128
+
129
+ ```bash
130
+ npx sck generate \
131
+ --contract CABC... \
132
+ --rpc-url https://my-rpc.example.com \
133
+ --passphrase "My Custom Network"
134
+ ```
135
+
136
+ ### CLI options
137
+
138
+ ```
139
+ Commands:
140
+ npx sck generate [options] Generate TypeScript types and example file
141
+ npx sck inspect [options] Print contract interface to the terminal
142
+ npx sck Interactive mode (arrow-key selection)
143
+
144
+ Shared options:
145
+ --contract Contract address (C..., 56 chars) [required]
146
+ --network testnet | mainnet | futurenet
147
+ --rpc-url Custom RPC URL
148
+ --passphrase Custom network passphrase
149
+
150
+ Generate-only options:
151
+ --out Output file path (default: ./contracts/<name>.ts)
152
+ --name Interface name (default: derived from --out)
153
+ --alias tsconfig path alias (auto-detected from tsconfig.json)
154
+ --js JavaScript output instead of TypeScript
155
+ --help, -h Show help
156
+ ```
157
+
158
+ ---
159
+
160
+ ## SDK
161
+
162
+ ### Quick start
163
+
164
+ ```ts
165
+ import { StellarContractsKit } from 'stellar-contracts-kit'
166
+
167
+ const kit = new StellarContractsKit({ network: 'testnet' })
168
+
169
+ // Opens built-in wallet picker modal
170
+ const { address } = await kit.connect()
171
+
172
+ // Load a contract client
173
+ const counter = await kit.contract('CABC...')
174
+
175
+ // Read-only (no wallet, no TX)
176
+ const { result } = await counter.get.read()
177
+
178
+ // Write (requires connected wallet)
179
+ const { txHash } = await counter.increment.invoke()
180
+ ```
181
+
182
+ ### With generated types
183
+
184
+ ```ts
185
+ import type { CounterContract } from './contracts/counter.js'
186
+ import { CONTRACT_ID } from './contracts/counter.js'
187
+
188
+ const counter = await kit.contract<CounterContract>(CONTRACT_ID)
189
+
190
+ // Full IDE autocomplete + type checking on all methods
191
+ const { result } = await counter.get.read()
192
+ const { txHash } = await counter.increment.invoke()
193
+ ```
194
+
195
+ ---
196
+
197
+ ## Wallet Connection
198
+
199
+ ### Built-in modal (recommended)
200
+
201
+ Calling `connect()` with no wallet configured opens a modal that auto-detects installed wallets:
202
+
203
+ ```ts
204
+ const kit = new StellarContractsKit({ network: 'testnet' })
205
+ const { address } = await kit.connect()
206
+ ```
207
+
208
+ The modal shows all supported wallets in order, with "Install" links for wallets that are not detected.
209
+
210
+ ### Specific wallet adapter
211
+
212
+ ```ts
213
+ import { StellarContractsKit, FreighterAdapter } from 'stellar-contracts-kit'
214
+
215
+ const kit = new StellarContractsKit({
216
+ network: 'testnet',
217
+ wallet: new FreighterAdapter(),
218
+ })
219
+ const { address } = await kit.connect()
220
+ ```
221
+
222
+ ### Supported wallets
223
+
224
+ | Wallet | Adapter | Install |
225
+ |--------|---------|---------|
226
+ | [Freighter](https://github.com/stellar/freighter) | `FreighterAdapter` | [freighter.app](https://freighter.app) |
227
+ | [Cyphras](https://github.com/cyphras/cyphras-extension) | `CyphrasAdapter` | [cyphras.com](https://cyphras.com) |
228
+ | [Lobstr](https://github.com/Lobstrco/lobstr-browser-extension) | `LobstrAdapter` | [lobstr.co](https://lobstr.co) |
229
+
230
+ ### Other wallet methods
231
+
232
+ ```ts
233
+ await kit.disconnect()
234
+ kit.isConnected() // boolean
235
+ await kit.getAddress() // string, throws WALLET_NOT_CONNECTED if none
236
+ kit.getWallet() // WalletAdapter | null
237
+ kit.setWallet(adapter) // replace active wallet
238
+ ```
239
+
240
+ ---
241
+
242
+ ## Contract Interaction
243
+
244
+ ### Call modes
245
+
246
+ Every method on the contract client has three modes:
247
+
248
+ ```ts
249
+ // Auto: simulates first, submits TX only if auth is required
250
+ const result = await counter.increment()
251
+
252
+ // Read: simulate only, no wallet needed, no TX submitted
253
+ const { result } = await counter.get.read()
254
+
255
+ // Invoke: always submits a TX, requires a connected wallet
256
+ const { txHash, result } = await counter.increment.invoke()
257
+ ```
258
+
259
+ ### Passing arguments
260
+
261
+ Arguments are passed positionally in the order defined by the contract:
262
+
263
+ ```ts
264
+ const { result } = await token.balance.read(userAddress)
265
+ const { txHash } = await token.transfer.invoke(from, to, amount)
266
+ ```
267
+
268
+ ### Spec caching
269
+
270
+ The contract spec is fetched once per contract ID and cached in memory. Subsequent `kit.contract()` calls with the same ID return instantly:
271
+
272
+ ```ts
273
+ const counter = await kit.contract<CounterContract>(CONTRACT_ID) // fetches spec
274
+ const counter2 = await kit.contract<CounterContract>(CONTRACT_ID) // instant
275
+
276
+ kit.clearSpecCache(CONTRACT_ID) // clear one
277
+ kit.clearSpecCache() // clear all
278
+ ```
279
+
280
+ ---
281
+
282
+ ## Custom Networks
283
+
284
+ ```ts
285
+ const kit = new StellarContractsKit({
286
+ network: {
287
+ rpcUrl: 'https://my-soroban-rpc.example.com',
288
+ networkPassphrase: 'My Custom Network',
289
+ horizonUrl: 'https://my-horizon.example.com',
290
+ },
291
+ })
292
+ ```
293
+
294
+ Built-in network presets:
295
+
296
+ ```ts
297
+ import { NETWORKS } from 'stellar-contracts-kit'
298
+
299
+ NETWORKS.testnet // Test SDF Network
300
+ NETWORKS.mainnet // Public Global Stellar Network
301
+ NETWORKS.futurenet // Test SDF Future Network
302
+ ```
303
+
304
+ ---
305
+
306
+ ## Error Handling
307
+
308
+ All errors thrown by the kit are `StellarContractError` instances with a `code` property:
309
+
310
+ ```ts
311
+ import { isContractKitError } from 'stellar-contracts-kit'
312
+
313
+ try {
314
+ await counter.increment.invoke()
315
+ } catch (err) {
316
+ if (isContractKitError(err)) {
317
+ switch (err.code) {
318
+ case 'WALLET_REJECTED':
319
+ console.log('User rejected the transaction.')
320
+ break
321
+ case 'CONTRACT_RESTORE_REQUIRED':
322
+ await kit.restoreContract(CONTRACT_ID)
323
+ await counter.increment.invoke() // retry
324
+ break
325
+ case 'TX_FAILED':
326
+ console.error('On-chain failure:', err.message)
327
+ break
328
+ }
329
+ }
330
+ }
331
+ ```
332
+
333
+ ### Error codes
334
+
335
+ | Code | Description |
336
+ |------|-------------|
337
+ | `WALLET_NOT_FOUND` | Wallet extension not installed |
338
+ | `WALLET_NOT_CONNECTED` | No wallet is connected |
339
+ | `WALLET_REJECTED` | User rejected the connection or signing request |
340
+ | `WALLET_NETWORK_MISMATCH` | Wallet is on a different network than the kit |
341
+ | `CONTRACT_NOT_FOUND` | Contract does not exist on the network |
342
+ | `CONTRACT_SPEC_ERROR` | Could not parse the contract WASM spec |
343
+ | `CONTRACT_SIMULATION_FAILED` | Transaction simulation failed |
344
+ | `CONTRACT_RESTORE_REQUIRED` | Contract state expired, call `restoreContract()` |
345
+ | `INVALID_CONTRACT_ID` | Invalid contract address format |
346
+ | `INVALID_PARAMS` | Wrong number of arguments passed to a method |
347
+ | `TX_SUBMISSION_FAILED` | Transaction rejected at submission |
348
+ | `TX_FAILED` | Transaction accepted but failed on-chain |
349
+ | `TX_TIMEOUT` | Transaction not confirmed within the polling window |
350
+ | `RPC_ERROR` | RPC call failed or returned an unexpected response |
351
+ | `UNKNOWN` | Unexpected error |
352
+
353
+ ---
354
+
355
+ ## Restoring Expired Contracts
356
+
357
+ Soroban contracts can expire when their TTL runs out. Any call that touches expired state throws `CONTRACT_RESTORE_REQUIRED`. Use `restoreContract()` to recover:
358
+
359
+ ```ts
360
+ try {
361
+ await counter.increment.invoke()
362
+ } catch (err) {
363
+ if (isContractKitError(err) && err.code === 'CONTRACT_RESTORE_REQUIRED') {
364
+ const { txHash } = await kit.restoreContract(CONTRACT_ID)
365
+ console.log('Restored:', txHash)
366
+ await counter.increment.invoke() // retry
367
+ }
368
+ }
369
+ ```
370
+
371
+ ---
372
+
373
+ ## Custom Wallet Adapter
374
+
375
+ Implement `WalletAdapter` to add support for any wallet:
376
+
377
+ ```ts
378
+ import type { WalletAdapter } from 'stellar-contracts-kit'
379
+
380
+ class MyWalletAdapter implements WalletAdapter {
381
+ readonly name = 'MyWallet'
382
+ readonly installUrl = 'https://mywallet.example.com'
383
+
384
+ isAvailable(): boolean | Promise<boolean> { ... }
385
+ async connect(): Promise<{ address: string }> { ... }
386
+ async disconnect(): Promise<void> { ... }
387
+ async getAddress(): Promise<string> { ... }
388
+ async getNetworkPassphrase(): Promise<string> { ... }
389
+ async signTransaction(xdr: string, opts): Promise<string> { ... }
390
+ async signAuthEntry(entryXdr: string, opts): Promise<string> { ... }
391
+ }
392
+ ```
393
+
394
+ ---
395
+
396
+ ## API Reference
397
+
398
+ ### `StellarContractsKit`
399
+
400
+ ```ts
401
+ new StellarContractsKit(options: StellarContractsKitOptions)
402
+ ```
403
+
404
+ | Method | Returns | Description |
405
+ |--------|---------|-------------|
406
+ | `connect()` | `Promise<{ address: string }>` | Connect to wallet or open picker modal |
407
+ | `disconnect()` | `Promise<void>` | Disconnect and clear the active wallet |
408
+ | `isConnected()` | `boolean` | True if a wallet is active |
409
+ | `getAddress()` | `Promise<string>` | Address of the connected wallet |
410
+ | `getWallet()` | `WalletAdapter \| null` | Active wallet adapter |
411
+ | `setWallet(adapter)` | `void` | Set or replace the active wallet |
412
+ | `getNetwork()` | `NetworkConfig` | Current network configuration |
413
+ | `contract<T>(id)` | `Promise<T>` | Load a typed contract client |
414
+ | `restoreContract(id)` | `Promise<{ txHash: string }>` | Restore expired contract state |
415
+ | `clearSpecCache(id?)` | `void` | Clear cached spec for one or all contracts |
416
+
417
+ ### `ContractMethodFn<TReturn, TArgs>`
418
+
419
+ The type for each method on a generated contract interface:
420
+
421
+ ```ts
422
+ ContractMethodFn<TReturn, TArgs extends unknown[] = unknown[]>
423
+ ```
424
+
425
+ | Type param | Description |
426
+ |------------|-------------|
427
+ | `TReturn` | Return value type (`number`, `bigint`, `string`, `void`, custom struct, ...) |
428
+ | `TArgs` | Labeled tuple of argument types (`[from: string, amount: bigint]`) |
429
+
430
+ ---
431
+
432
+ ## License
433
+
434
+ [MIT](LICENSE)