@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/dist/sdk.js ADDED
@@ -0,0 +1,239 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.CitrateSDK = void 0;
7
+ const events_1 = require("events");
8
+ const axios_1 = __importDefault(require("axios"));
9
+ const model_1 = require("./model");
10
+ const contract_1 = require("./contract");
11
+ const account_1 = require("./account");
12
+ /**
13
+ * Main Citrate SDK class
14
+ */
15
+ class CitrateSDK extends events_1.EventEmitter {
16
+ constructor(config) {
17
+ super();
18
+ const rpcEndpoint = config.rpcEndpoint ?? 'http://localhost:8545';
19
+ const chainId = config.chainId ?? 1337;
20
+ this.config = { ...config, rpcEndpoint, chainId };
21
+ const headers = {
22
+ 'Content-Type': 'application/json'
23
+ };
24
+ if (this.config.apiKey) {
25
+ headers['Authorization'] = `Bearer ${this.config.apiKey}`;
26
+ }
27
+ this.rpcClient = axios_1.default.create({
28
+ baseURL: this.config.rpcEndpoint,
29
+ headers
30
+ });
31
+ // Initialize managers
32
+ this.models = new model_1.ModelRegistry(this.rpcClient, this.config);
33
+ this.contracts = new contract_1.ContractManager(this.rpcClient, this.config);
34
+ this.accounts = new account_1.AccountManager(this.rpcClient, this.config);
35
+ }
36
+ /**
37
+ * Deploy a model to the network
38
+ */
39
+ async deployModel(modelData, metadata) {
40
+ return this.models.deploy(modelData, metadata);
41
+ }
42
+ /**
43
+ * Run inference on a deployed model
44
+ */
45
+ async runInference(modelId, input, options) {
46
+ return this.models.runInference(modelId, input, options);
47
+ }
48
+ /**
49
+ * Get proof for an execution
50
+ */
51
+ async getProof(executionId) {
52
+ const response = await this.rpcCall('citrate_getProof', [{
53
+ execution_id: executionId
54
+ }]);
55
+ return response.proof;
56
+ }
57
+ /**
58
+ * Verify a proof
59
+ */
60
+ async verifyProof(proof, expectedOutput) {
61
+ const response = await this.rpcCall('citrate_verifyProof', [{
62
+ proof,
63
+ output_hash: expectedOutput
64
+ }]);
65
+ return response.valid;
66
+ }
67
+ // ======= Artifacts =======
68
+ async pinArtifact(cid, replicas = 1) {
69
+ const response = await this.rpcClient.post('', {
70
+ jsonrpc: '2.0', method: 'citrate_pinArtifact', params: [cid, replicas], id: Date.now()
71
+ });
72
+ if (response.data.error)
73
+ throw new Error(response.data.error.message);
74
+ return response.data.result;
75
+ }
76
+ async getArtifactStatus(cid) {
77
+ const response = await this.rpcClient.post('', {
78
+ jsonrpc: '2.0', method: 'citrate_getArtifactStatus', params: [cid], id: Date.now()
79
+ });
80
+ if (response.data.error)
81
+ throw new Error(response.data.error.message);
82
+ const result = response.data.result;
83
+ try {
84
+ return JSON.parse(typeof result === 'string' ? result : JSON.stringify(result));
85
+ }
86
+ catch {
87
+ return [];
88
+ }
89
+ }
90
+ async listModelArtifacts(modelIdHex) {
91
+ const response = await this.rpcClient.post('', {
92
+ jsonrpc: '2.0', method: 'citrate_listModelArtifacts', params: [modelIdHex], id: Date.now()
93
+ });
94
+ if (response.data.error)
95
+ throw new Error(response.data.error.message);
96
+ const result = response.data.result;
97
+ return Array.isArray(result) ? result : [];
98
+ }
99
+ /**
100
+ * Get network information
101
+ */
102
+ async getNetworkInfo() {
103
+ const [networkId, chainIdHex, blockNumber, syncing, peerCount] = await Promise.all([
104
+ this.rpcCall('net_version'),
105
+ this.rpcCall('eth_chainId'),
106
+ this.rpcCall('eth_blockNumber'),
107
+ this.rpcCall('eth_syncing'),
108
+ this.rpcCall('net_peerCount')
109
+ ]);
110
+ return {
111
+ networkId: parseInt(networkId),
112
+ chainId: parseInt(chainIdHex, 16),
113
+ blockNumber: parseInt(blockNumber, 16),
114
+ syncing: typeof syncing === 'object',
115
+ peerCount: parseInt(peerCount, 16)
116
+ };
117
+ }
118
+ /**
119
+ * Get block information
120
+ */
121
+ async getBlock(blockNumber) {
122
+ const tag = blockNumber === 'latest' ? 'latest' : `0x${blockNumber.toString(16)}`;
123
+ // For "latest", retry with a small delay if the RPC returns null.
124
+ // This handles the race condition where get_latest_height() reports a
125
+ // height whose block data hasn't been fully committed yet.
126
+ let block = await this.rpcCall('eth_getBlockByNumber', [tag, true]);
127
+ if (!block && blockNumber === 'latest') {
128
+ for (let attempt = 0; attempt < 3 && !block; attempt++) {
129
+ await new Promise(r => setTimeout(r, 200));
130
+ block = await this.rpcCall('eth_getBlockByNumber', [tag, true]);
131
+ }
132
+ }
133
+ if (!block) {
134
+ throw new Error(`Block not found: ${blockNumber}`);
135
+ }
136
+ return {
137
+ number: parseInt(block.number, 16),
138
+ hash: block.hash,
139
+ parentHash: block.parentHash,
140
+ timestamp: parseInt(block.timestamp, 16),
141
+ miner: block.miner,
142
+ transactions: block.transactions,
143
+ // GhostDAG specific
144
+ mergeParents: block.mergeParents || [],
145
+ blueScore: block.blueScore ? parseInt(block.blueScore, 16) : undefined
146
+ };
147
+ }
148
+ /**
149
+ * Get DAG statistics
150
+ */
151
+ async getDagStats() {
152
+ const stats = await this.rpcCall('citrate_getDagStats');
153
+ return {
154
+ totalBlocks: stats.totalBlocks,
155
+ blueBlocks: stats.blueBlocks,
156
+ redBlocks: stats.redBlocks,
157
+ tipsCount: stats.tipsCount,
158
+ maxBlueScore: stats.maxBlueScore,
159
+ currentTips: stats.currentTips || [],
160
+ height: stats.height,
161
+ ghostdagParams: stats.ghostdagParams
162
+ };
163
+ }
164
+ /**
165
+ * Subscribe to new blocks
166
+ */
167
+ subscribeToBlocks(callback) {
168
+ let intervalId;
169
+ let lastBlockNumber = 0;
170
+ const checkNewBlock = async () => {
171
+ try {
172
+ const block = await this.getBlock('latest');
173
+ if (block.number > lastBlockNumber) {
174
+ lastBlockNumber = block.number;
175
+ callback(block);
176
+ this.emit('block', block);
177
+ }
178
+ }
179
+ catch (error) {
180
+ this.emit('error', error);
181
+ }
182
+ };
183
+ // Poll for new blocks
184
+ intervalId = setInterval(checkNewBlock, 2000);
185
+ checkNewBlock(); // Check immediately
186
+ // Return unsubscribe function
187
+ return () => {
188
+ clearInterval(intervalId);
189
+ };
190
+ }
191
+ /**
192
+ * Make an RPC call
193
+ */
194
+ async rpcCall(method, params = []) {
195
+ const response = await this.rpcClient.post('', {
196
+ jsonrpc: '2.0',
197
+ method,
198
+ params: Array.isArray(params) ? params : [params],
199
+ id: Date.now()
200
+ });
201
+ if (response.data.error) {
202
+ throw new Error(response.data.error.message);
203
+ }
204
+ return response.data.result;
205
+ }
206
+ /**
207
+ * Wait for a transaction to be mined
208
+ */
209
+ async waitForTransaction(txHash, confirmations = 1) {
210
+ let receipt = null;
211
+ let attempts = 0;
212
+ const maxAttempts = 60;
213
+ while (!receipt && attempts < maxAttempts) {
214
+ attempts++;
215
+ try {
216
+ receipt = await this.rpcCall('eth_getTransactionReceipt', [txHash]);
217
+ if (receipt && confirmations > 1) {
218
+ // Wait for additional confirmations
219
+ const currentBlock = await this.getBlock('latest');
220
+ const txBlock = parseInt(receipt.blockNumber, 16);
221
+ if (currentBlock.number - txBlock < confirmations - 1) {
222
+ receipt = null; // Keep waiting
223
+ }
224
+ }
225
+ }
226
+ catch (error) {
227
+ // Transaction not yet mined
228
+ }
229
+ if (!receipt) {
230
+ await new Promise(resolve => setTimeout(resolve, 2000));
231
+ }
232
+ }
233
+ if (!receipt) {
234
+ throw new Error('Transaction timeout');
235
+ }
236
+ return receipt;
237
+ }
238
+ }
239
+ exports.CitrateSDK = CitrateSDK;
@@ -0,0 +1,63 @@
1
+ export interface CitrateConfig {
2
+ rpcEndpoint: string;
3
+ chainId: number;
4
+ defaultAccount?: string;
5
+ gasPrice?: string;
6
+ gasLimit?: number;
7
+ /** API key for authenticated RPC endpoints. Sent as Authorization: Bearer header. */
8
+ apiKey?: string;
9
+ }
10
+ export interface ArtifactProviderStatus {
11
+ provider: string;
12
+ status: 'pinned' | 'unpinned' | 'unknown';
13
+ }
14
+ export interface ArtifactPinResult {
15
+ status: 'ok' | 'error';
16
+ message?: string;
17
+ }
18
+ export interface NetworkInfo {
19
+ networkId: number;
20
+ chainId: number;
21
+ blockNumber: number;
22
+ syncing: boolean;
23
+ peerCount: number;
24
+ }
25
+ export interface BlockInfo {
26
+ number: number;
27
+ hash: string;
28
+ parentHash: string;
29
+ timestamp: number;
30
+ miner: string;
31
+ transactions: any[];
32
+ mergeParents?: string[];
33
+ blueScore?: number;
34
+ }
35
+ export interface ModelMetadata {
36
+ name: string;
37
+ version: string;
38
+ format: string;
39
+ description?: string;
40
+ author?: string;
41
+ license?: string;
42
+ tags?: string[];
43
+ }
44
+ export interface ModelInfo {
45
+ id: string;
46
+ owner: string;
47
+ metadata: ModelMetadata;
48
+ dataHash: string;
49
+ timestamp: number;
50
+ permissions: string[];
51
+ }
52
+ export interface InferenceResult {
53
+ output: any;
54
+ executionTime: number;
55
+ proofId?: string;
56
+ gasUsed?: number;
57
+ }
58
+ export interface ContractInfo {
59
+ address: string;
60
+ bytecode?: string;
61
+ abi?: any[];
62
+ verified?: boolean;
63
+ }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json ADDED
@@ -0,0 +1,79 @@
1
+ {
2
+ "name": "@citrate/sdk",
3
+ "version": "0.2.0",
4
+ "description": "JavaScript/TypeScript SDK for Citrate AI blockchain platform",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.esm.js",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist",
10
+ "README.md",
11
+ "LICENSE"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsc && rollup -c",
15
+ "build:types": "tsc --emitDeclarationOnly",
16
+ "test": "jest --testPathIgnorePatterns=tests/integration",
17
+ "test:unit": "jest --testPathIgnorePatterns=tests/integration",
18
+ "test:integration": "jest --testMatch='**/tests/integration/**/*.test.ts' --runInBand --testTimeout=120000",
19
+ "test:integration:devnet": "CITRATE_RPC_URL=http://localhost:8545 CITRATE_CHAIN_ID=1337 npm run test:integration",
20
+ "test:integration:testnet": "CITRATE_RPC_URL=https://testnet-rpc.citrate.ai CITRATE_CHAIN_ID=1338 npm run test:integration",
21
+ "test:all": "jest --testTimeout=120000",
22
+ "test:coverage": "jest --coverage --testPathIgnorePatterns=tests/integration",
23
+ "lint": "eslint src/**/*.ts",
24
+ "lint:fix": "eslint src/**/*.ts --fix",
25
+ "format": "prettier --write \"src/**/*.{ts,js,json}\"",
26
+ "prepublishOnly": "npm run build",
27
+ "release": "npm run build && npm publish --access public"
28
+ },
29
+ "keywords": [
30
+ "citrate",
31
+ "blockchain",
32
+ "ai",
33
+ "dag",
34
+ "web3",
35
+ "machine-learning",
36
+ "distributed-computing",
37
+ "cryptocurrency"
38
+ ],
39
+ "author": "Citrate Team <developers@citrate.ai>",
40
+ "license": "MIT",
41
+ "homepage": "https://citrate.ai",
42
+ "repository": {
43
+ "type": "git",
44
+ "url": "https://github.com/citrate-ai/citrate.git",
45
+ "directory": "sdk/javascript"
46
+ },
47
+ "bugs": {
48
+ "url": "https://github.com/citrate-ai/citrate/issues"
49
+ },
50
+ "engines": {
51
+ "node": ">=16.0.0"
52
+ },
53
+ "dependencies": {
54
+ "axios": "^1.6.0",
55
+ "ethers": "^6.9.0",
56
+ "events": "^3.3.0",
57
+ "ws": "^8.14.0"
58
+ },
59
+ "devDependencies": {
60
+ "@rollup/plugin-commonjs": "^25.0.0",
61
+ "@rollup/plugin-node-resolve": "^15.2.0",
62
+ "@rollup/plugin-typescript": "^11.1.0",
63
+ "@types/jest": "^29.5.0",
64
+ "@types/node": "^20.10.0",
65
+ "@types/ws": "^8.5.0",
66
+ "@typescript-eslint/eslint-plugin": "^6.14.0",
67
+ "@typescript-eslint/parser": "^6.14.0",
68
+ "eslint": "^8.55.0",
69
+ "jest": "^29.7.0",
70
+ "prettier": "^3.1.0",
71
+ "rollup": "^4.6.0",
72
+ "ts-jest": "^29.1.0",
73
+ "typescript": "^5.3.0"
74
+ },
75
+ "publishConfig": {
76
+ "access": "public",
77
+ "registry": "https://registry.npmjs.org/"
78
+ }
79
+ }