@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 +284 -0
- package/dist/account.d.ts +59 -0
- package/dist/account.js +145 -0
- package/dist/contract.d.ts +67 -0
- package/dist/contract.js +207 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.esm.js +13 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/model.d.ts +38 -0
- package/dist/model.js +91 -0
- package/dist/sdk.d.ts +86 -0
- package/dist/sdk.js +239 -0
- package/dist/types.d.ts +63 -0
- package/dist/types.js +2 -0
- package/package.json +79 -0
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;
|
package/dist/types.d.ts
ADDED
|
@@ -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
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
|
+
}
|