@citrate/sdk 0.2.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/README.md ADDED
@@ -0,0 +1,284 @@
1
+ # Citrate JavaScript/TypeScript SDK
2
+
3
+ The official JavaScript/TypeScript SDK for the Citrate AI blockchain platform. This SDK provides a comprehensive interface for interacting with Citrate nodes, deploying AI models, managing accounts, and executing transactions.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @citrate-ai/sdk
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```typescript
14
+ import { CitrateSDK } from '@citrate-ai/sdk';
15
+
16
+ // Initialize the SDK (JSON-RPC endpoint)
17
+ const sdk = new CitrateSDK({
18
+ rpcEndpoint: 'http://localhost:8545',
19
+ chainId: 1337,
20
+ });
21
+
22
+ // Import an account (enables raw-tx signing)
23
+ const address = sdk.accounts.importAccount('<PRIVATE_KEY_HEX>');
24
+
25
+ // Get balance
26
+ const balance = await sdk.accounts.getBalance(address);
27
+ console.log(`Balance (wei): ${balance}`);
28
+
29
+ // List models and run inference
30
+ const ids: string[] = await sdk.models.listModels();
31
+ if (ids.length) {
32
+ const info = await sdk.models.getModel(ids[0]);
33
+ console.log('Model:', info);
34
+ const result = await sdk.models.runInference(ids[0], { text: 'hello lattice' });
35
+ console.log('Inference:', result);
36
+ }
37
+ ```
38
+
39
+ ## Features
40
+
41
+ ### 🔗 Node Connectivity
42
+ - Connect to Citrate nodes via RPC/WebSocket
43
+ - Real-time event listening
44
+ - Automatic reconnection handling
45
+
46
+ ### 🤖 AI Model Management
47
+ - Deploy models to the blockchain
48
+ - Execute model inference
49
+ - Model versioning and metadata
50
+ - IPFS integration for model storage
51
+
52
+ ### 💰 Account Management
53
+ - Wallet integration (MetaMask, WalletConnect)
54
+ - Account creation and import
55
+ - Balance queries
56
+ - Transaction signing
57
+
58
+ ### 🔄 Transaction Management
59
+ - Send transactions
60
+ - Smart contract interaction
61
+ - Gas estimation
62
+ - Transaction status tracking
63
+
64
+ ### 📊 DAG Explorer
65
+ - Query block DAG structure
66
+ - Get block information
67
+ - Explore transaction history
68
+ - Real-time chain updates
69
+
70
+ ## API Reference
71
+
72
+ ### CitrateSDK
73
+
74
+ Main SDK class that provides access to all functionality.
75
+
76
+ ```typescript
77
+ const sdk = new CitrateSDK({
78
+ nodeUrl: string,
79
+ chainId?: number,
80
+ timeout?: number,
81
+ retries?: number,
82
+ });
83
+ ```
84
+
85
+ ### Models
86
+
87
+ Model deployment and management.
88
+
89
+ ```typescript
90
+ // Deploy a new model
91
+ await sdk.models.deploy({
92
+ name: string,
93
+ description: string,
94
+ ipfsHash: string,
95
+ framework: 'pytorch' | 'tensorflow' | 'onnx',
96
+ version: string,
97
+ accessType: 'public' | 'private',
98
+ price?: string,
99
+ });
100
+
101
+ // Execute model inference
102
+ const result = await sdk.models.execute(modelId, {
103
+ inputs: any[],
104
+ outputFormat: 'json' | 'binary',
105
+ });
106
+
107
+ // Get model information
108
+ const modelInfo = await sdk.models.getInfo(modelId);
109
+ ```
110
+
111
+ ### Accounts
112
+
113
+ Account and wallet management.
114
+
115
+ ```typescript
116
+ // Connect wallet
117
+ await sdk.account.connectWallet();
118
+
119
+ // Create new account
120
+ const account = await sdk.account.create();
121
+
122
+ // Get balance
123
+ const balance = await sdk.account.getBalance(address?);
124
+
125
+ // Send transaction
126
+ const txHash = await sdk.account.sendTransaction({
127
+ to: string,
128
+ value: string,
129
+ data?: string,
130
+ gasLimit?: number,
131
+ });
132
+ ```
133
+
134
+ ### Contracts
135
+
136
+ Smart contract interaction.
137
+
138
+ ```typescript
139
+ // Deploy contract
140
+ const contractAddress = await sdk.contracts.deploy({
141
+ bytecode: string,
142
+ abi: any[],
143
+ constructorArgs?: any[],
144
+ });
145
+
146
+ // Call contract method
147
+ const result = await sdk.contracts.call({
148
+ address: string,
149
+ abi: any[],
150
+ method: string,
151
+ args: any[],
152
+ });
153
+
154
+ // Send contract transaction
155
+ const txHash = await sdk.contracts.send({
156
+ address: string,
157
+ abi: any[],
158
+ method: string,
159
+ args: any[],
160
+ value?: string,
161
+ });
162
+ ```
163
+
164
+ ## Advanced Usage
165
+
166
+ ### Event Listening
167
+
168
+ ```typescript
169
+ // Listen for new blocks
170
+ sdk.on('block', (block) => {
171
+ console.log(`New block: ${block.hash}`);
172
+ });
173
+
174
+ // Listen for model deployments
175
+ sdk.on('modelDeployed', (event) => {
176
+ console.log(`Model deployed: ${event.modelId}`);
177
+ });
178
+
179
+ // Listen for transactions
180
+ sdk.on('transaction', (tx) => {
181
+ console.log(`Transaction: ${tx.hash}`);
182
+ });
183
+ ```
184
+
185
+ ### Custom Providers
186
+
187
+ ```typescript
188
+ import { ethers } from 'ethers';
189
+
190
+ // Use custom provider
191
+ const provider = new ethers.providers.WebSocketProvider('ws://localhost:8546');
192
+ const sdk = new CitrateSDK({
193
+ provider,
194
+ chainId: 1337,
195
+ });
196
+ ```
197
+
198
+ ### Batch Operations
199
+
200
+ ```typescript
201
+ // Batch multiple model executions
202
+ const results = await sdk.models.batchExecute([
203
+ { modelId: 'model1', inputs: [1, 2, 3] },
204
+ { modelId: 'model2', inputs: [4, 5, 6] },
205
+ ]);
206
+ ```
207
+
208
+ ## Configuration
209
+
210
+ ### Environment Variables
211
+
212
+ ```bash
213
+ CITRATE_NODE_URL=http://localhost:8545
214
+ CITRATE_CHAIN_ID=1337
215
+ CITRATE_TIMEOUT=30000
216
+ CITRATE_RETRIES=3
217
+ ```
218
+
219
+ ### TypeScript Configuration
220
+
221
+ ```json
222
+ {
223
+ "compilerOptions": {
224
+ "target": "ES2018",
225
+ "module": "ESNext",
226
+ "moduleResolution": "node",
227
+ "strict": true,
228
+ "esModuleInterop": true,
229
+ "skipLibCheck": true,
230
+ "declaration": true
231
+ }
232
+ }
233
+ ```
234
+
235
+ ## Error Handling
236
+
237
+ ```typescript
238
+ try {
239
+ const ids = await sdk.models.listModels();
240
+ if (!ids.length) throw new Error('No models registered');
241
+ const result = await sdk.models.runInference(ids[0], { text: 'hello' });
242
+ console.log(result.output);
243
+ } catch (e) {
244
+ console.error('SDK/RPC error:', e);
245
+ }
246
+ ```
247
+
248
+ ## Signing Best Practices
249
+
250
+ - Prefer client-side signing (raw tx) for public/testnet RPCs. Import a private key or mnemonic via `sdk.accounts.importAccount(...)`.
251
+ - In local development, the node can accept `eth_sendTransaction` without a valid signature only if started with `CITRATE_REQUIRE_VALID_SIGNATURE=false`.
252
+
253
+ ## Testing
254
+
255
+ ```bash
256
+ npm test # Run all tests
257
+ npm run test:unit # Run unit tests
258
+ npm run test:integration # Run integration tests
259
+ npm run test:coverage # Run with coverage
260
+ ```
261
+
262
+ ## Contributing
263
+
264
+ 1. Fork the repository
265
+ 2. Create a feature branch
266
+ 3. Make your changes
267
+ 4. Add tests for new functionality
268
+ 5. Run the test suite
269
+ 6. Submit a pull request
270
+
271
+ ## License
272
+
273
+ Apache-2.0 License. See [LICENSE](../../LICENSE) for details.
274
+
275
+ ## Support
276
+
277
+ - 📖 [Documentation](https://docs.citrate.ai)
278
+ - 💬 [Discord Community](https://discord.gg/lattice-ai)
279
+ - 🐛 [Issue Tracker](https://github.com/lattice-ai/citrate/issues)
280
+ - 📧 [Email Support](mailto:support@citrate.ai)
281
+
282
+ ## Changelog
283
+
284
+ See [CHANGELOG.md](./CHANGELOG.md) for version history and updates.
@@ -0,0 +1,59 @@
1
+ import { AxiosInstance } from 'axios';
2
+ import { CitrateConfig } from './types';
3
+ export declare class AccountManager {
4
+ private rpcClient;
5
+ private config;
6
+ private wallet?;
7
+ constructor(rpcClient: AxiosInstance, config: CitrateConfig);
8
+ /**
9
+ * Create a new account
10
+ */
11
+ createAccount(): {
12
+ address: string;
13
+ privateKey: string;
14
+ mnemonic?: string;
15
+ };
16
+ /**
17
+ * Import account from private key
18
+ */
19
+ importAccount(privateKey: string): string;
20
+ /**
21
+ * Import account from mnemonic
22
+ */
23
+ importFromMnemonic(mnemonic: string, path?: string): string;
24
+ /**
25
+ * Get account balance
26
+ */
27
+ getBalance(address?: string): Promise<bigint>;
28
+ /**
29
+ * Get account nonce
30
+ */
31
+ getNonce(address?: string): Promise<number>;
32
+ /**
33
+ * Send transaction
34
+ */
35
+ sendTransaction(tx: {
36
+ to: string;
37
+ value?: string;
38
+ data?: string;
39
+ gasLimit?: number;
40
+ gasPrice?: string;
41
+ }): Promise<string>;
42
+ /**
43
+ * Sign and send transaction
44
+ */
45
+ private sendSignedTransaction;
46
+ /**
47
+ * Sign message
48
+ */
49
+ signMessage(message: string): Promise<string>;
50
+ /**
51
+ * Verify message signature
52
+ */
53
+ verifyMessage(message: string, signature: string, address: string): boolean;
54
+ /**
55
+ * List accounts (from node)
56
+ */
57
+ listAccounts(): Promise<string[]>;
58
+ private rpcCall;
59
+ }
@@ -0,0 +1,145 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AccountManager = void 0;
4
+ const ethers_1 = require("ethers");
5
+ class AccountManager {
6
+ constructor(rpcClient, config) {
7
+ this.rpcClient = rpcClient;
8
+ this.config = config;
9
+ }
10
+ /**
11
+ * Create a new account
12
+ */
13
+ createAccount() {
14
+ const wallet = ethers_1.ethers.Wallet.createRandom();
15
+ return {
16
+ address: wallet.address,
17
+ privateKey: wallet.privateKey,
18
+ mnemonic: wallet.mnemonic?.phrase
19
+ };
20
+ }
21
+ /**
22
+ * Import account from private key
23
+ */
24
+ importAccount(privateKey) {
25
+ const wallet = new ethers_1.ethers.Wallet(privateKey);
26
+ this.wallet = wallet;
27
+ this.config.defaultAccount = wallet.address;
28
+ return wallet.address;
29
+ }
30
+ /**
31
+ * Import account from mnemonic
32
+ */
33
+ importFromMnemonic(mnemonic, path = "m/44'/60'/0'/0/0") {
34
+ // In ethers v6, fromPhrase already derives the default BIP-44 path internally.
35
+ // Use fromMnemonic for explicit path control to avoid double-derivation.
36
+ const mnemonicObj = ethers_1.ethers.Mnemonic.fromPhrase(mnemonic);
37
+ const wallet = ethers_1.ethers.HDNodeWallet.fromMnemonic(mnemonicObj, path);
38
+ this.wallet = wallet;
39
+ this.config.defaultAccount = wallet.address;
40
+ return wallet.address;
41
+ }
42
+ /**
43
+ * Get account balance
44
+ */
45
+ async getBalance(address) {
46
+ const addr = address || this.config.defaultAccount;
47
+ if (!addr) {
48
+ throw new Error('No address specified');
49
+ }
50
+ const response = await this.rpcCall('eth_getBalance', [addr, 'latest']);
51
+ return BigInt(response);
52
+ }
53
+ /**
54
+ * Get account nonce
55
+ */
56
+ async getNonce(address) {
57
+ const addr = address || this.config.defaultAccount;
58
+ if (!addr) {
59
+ throw new Error('No address specified');
60
+ }
61
+ const response = await this.rpcCall('eth_getTransactionCount', [addr, 'latest']);
62
+ return parseInt(response, 16);
63
+ }
64
+ /**
65
+ * Send transaction
66
+ */
67
+ async sendTransaction(tx) {
68
+ if (!this.config.defaultAccount) {
69
+ throw new Error('No default account set');
70
+ }
71
+ const nonce = await this.getNonce();
72
+ const txData = {
73
+ from: this.config.defaultAccount,
74
+ to: tx.to,
75
+ value: tx.value || '0x0',
76
+ data: tx.data || '0x',
77
+ gas: `0x${(tx.gasLimit || this.config.gasLimit || 21000).toString(16)}`,
78
+ gasPrice: tx.gasPrice || this.config.gasPrice || '0x3b9aca00',
79
+ nonce: `0x${nonce.toString(16)}`
80
+ };
81
+ // If we have a wallet, sign the transaction
82
+ if (this.wallet) {
83
+ return await this.sendSignedTransaction(txData);
84
+ }
85
+ // Otherwise, send unsigned (requires unlocked account on node)
86
+ return await this.rpcCall('eth_sendTransaction', [txData]);
87
+ }
88
+ /**
89
+ * Sign and send transaction
90
+ */
91
+ async sendSignedTransaction(txData) {
92
+ if (!this.wallet) {
93
+ throw new Error('No wallet available for signing');
94
+ }
95
+ // Create transaction object for ethers
96
+ const tx = {
97
+ to: txData.to,
98
+ value: txData.value,
99
+ data: txData.data,
100
+ gasLimit: txData.gas,
101
+ gasPrice: txData.gasPrice,
102
+ nonce: parseInt(txData.nonce, 16),
103
+ chainId: this.config.chainId
104
+ };
105
+ // Sign transaction
106
+ const signedTx = await this.wallet.signTransaction(tx);
107
+ // Send raw transaction
108
+ return await this.rpcCall('eth_sendRawTransaction', [signedTx]);
109
+ }
110
+ /**
111
+ * Sign message
112
+ */
113
+ async signMessage(message) {
114
+ if (!this.wallet) {
115
+ throw new Error('No wallet available for signing');
116
+ }
117
+ return await this.wallet.signMessage(message);
118
+ }
119
+ /**
120
+ * Verify message signature
121
+ */
122
+ verifyMessage(message, signature, address) {
123
+ const recoveredAddress = ethers_1.ethers.verifyMessage(message, signature);
124
+ return recoveredAddress.toLowerCase() === address.toLowerCase();
125
+ }
126
+ /**
127
+ * List accounts (from node)
128
+ */
129
+ async listAccounts() {
130
+ return await this.rpcCall('eth_accounts');
131
+ }
132
+ async rpcCall(method, params = []) {
133
+ const response = await this.rpcClient.post('', {
134
+ jsonrpc: '2.0',
135
+ method,
136
+ params,
137
+ id: Date.now()
138
+ });
139
+ if (response.data.error) {
140
+ throw new Error(response.data.error.message);
141
+ }
142
+ return response.data.result;
143
+ }
144
+ }
145
+ exports.AccountManager = AccountManager;
@@ -0,0 +1,67 @@
1
+ import { AxiosInstance } from 'axios';
2
+ import { CitrateConfig, ContractInfo } from './types';
3
+ export declare class ContractManager {
4
+ private rpcClient;
5
+ private config;
6
+ constructor(rpcClient: AxiosInstance, config: CitrateConfig);
7
+ /**
8
+ * Deploy a contract
9
+ */
10
+ deploy(bytecode: string, abi?: any[], constructorArgs?: any[], value?: string): Promise<string>;
11
+ /**
12
+ * Call a contract method (transaction)
13
+ */
14
+ call(address: string, abi: any[], method: string, args?: any[], value?: string): Promise<any>;
15
+ /**
16
+ * Read contract state (call without transaction)
17
+ */
18
+ read(address: string, abi: any[], method: string, args?: any[]): Promise<any>;
19
+ /**
20
+ * Get contract code
21
+ */
22
+ getCode(address: string): Promise<string>;
23
+ /**
24
+ * Get contract info
25
+ */
26
+ getContractInfo(address: string): Promise<ContractInfo>;
27
+ /**
28
+ * Verify contract source code
29
+ */
30
+ verify(address: string, sourceCode: string, compilerVersion: string, optimizationEnabled?: boolean): Promise<string>;
31
+ /**
32
+ * Create contract instance with ABI
33
+ */
34
+ createInstance(address: string, abi: any[]): ContractInstance;
35
+ private waitForReceipt;
36
+ rpcCall(method: string, params: any[]): Promise<any>;
37
+ }
38
+ /**
39
+ * Contract instance helper class
40
+ */
41
+ export declare class ContractInstance {
42
+ address: string;
43
+ abi: any[];
44
+ private manager;
45
+ private iface;
46
+ constructor(address: string, abi: any[], manager: ContractManager);
47
+ /**
48
+ * Call a method (transaction)
49
+ */
50
+ send(method: string, args?: any[], value?: string): Promise<any>;
51
+ /**
52
+ * Read state (no transaction)
53
+ */
54
+ call(method: string, args?: any[]): Promise<any>;
55
+ /**
56
+ * Encode method call data
57
+ */
58
+ encodeCall(method: string, args?: any[]): string;
59
+ /**
60
+ * Decode method result
61
+ */
62
+ decodeResult(method: string, data: string): any;
63
+ /**
64
+ * Parse event logs
65
+ */
66
+ parseLogs(logs: any[]): any[];
67
+ }