jaelis-node 1.4.1 → 1.7.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/lib/index.js CHANGED
@@ -485,6 +485,9 @@ class JaelisNode extends EventEmitter {
485
485
  await this._initRpcServer();
486
486
  }
487
487
 
488
+ // Start Settlement Server for cross-chain deployments
489
+ await this._initSettlementServer();
490
+
488
491
  this.isRunning = true;
489
492
  this.startTime = Date.now();
490
493
 
@@ -522,6 +525,11 @@ class JaelisNode extends EventEmitter {
522
525
  // Stop heartbeat first
523
526
  this._stopHeartbeat();
524
527
 
528
+ // Stop Settlement server
529
+ if (this.settlementServer) {
530
+ await this._stopSettlementServer();
531
+ }
532
+
525
533
  // Stop RPC server
526
534
  if (this.rpcServer) {
527
535
  await this._stopRpcServer();
@@ -623,7 +631,8 @@ class JaelisNode extends EventEmitter {
623
631
  console.log('[JAELIS] Initializing RPC server...');
624
632
 
625
633
  if (this.JaelisRpcServer) {
626
- this.rpcServer = new this.JaelisRpcServer(this.blockchain, this.options.rpcPort);
634
+ // Pass network reference for sync status reporting
635
+ this.rpcServer = new this.JaelisRpcServer(this.blockchain, this.options.rpcPort, this.network);
627
636
 
628
637
  if (this.rpcServer.start) {
629
638
  await this.rpcServer.start();
@@ -635,6 +644,52 @@ class JaelisNode extends EventEmitter {
635
644
  console.log(`[JAELIS] RPC server started on http://${this.options.rpcHost}:${this.options.rpcPort}`);
636
645
  }
637
646
 
647
+ /**
648
+ * Initialize Settlement Server for cross-chain deployments
649
+ *
650
+ * The Settlement Server handles propagating JAELIS contract deployments
651
+ * to external chains. NO GAS - NO FEES - NO SANDBOX - NO WALLS!
652
+ */
653
+ async _initSettlementServer() {
654
+ console.log('[JAELIS] Initializing Settlement Server...');
655
+
656
+ try {
657
+ // Load the settlement server module
658
+ const settlementPath = path.join(__dirname, 'settlement-server.js');
659
+
660
+ if (fs.existsSync(settlementPath)) {
661
+ const { SettlementServer } = require(settlementPath);
662
+
663
+ this.settlementServer = new SettlementServer({
664
+ port: this.options.settlementPort || 3847,
665
+ host: this.options.rpcHost || '0.0.0.0'
666
+ });
667
+
668
+ await this.settlementServer.start();
669
+
670
+ console.log(`[JAELIS] Settlement Server started on port ${this.options.settlementPort || 3847}`);
671
+ console.log('[JAELIS] Cross-chain deployments: ENABLED');
672
+ console.log('[JAELIS] NO GAS - NO FEES - NO SANDBOX - NO WALLS!');
673
+ } else {
674
+ console.log('[JAELIS] Settlement Server not found, cross-chain disabled');
675
+ }
676
+ } catch (error) {
677
+ // Don't fail node startup if settlement server fails
678
+ console.error('[JAELIS] Settlement Server error:', error.message);
679
+ console.log('[JAELIS] Continuing without cross-chain support...');
680
+ }
681
+ }
682
+
683
+ /**
684
+ * Stop Settlement Server
685
+ */
686
+ async _stopSettlementServer() {
687
+ if (this.settlementServer && this.settlementServer.stop) {
688
+ await this.settlementServer.stop();
689
+ console.log('[JAELIS] Settlement Server stopped');
690
+ }
691
+ }
692
+
638
693
  /**
639
694
  * Stop RPC server
640
695
  */
@@ -1042,6 +1097,82 @@ class EmbeddedBlockchain {
1042
1097
  return this.vmExecutor?.getAllContracts?.() || [];
1043
1098
  }
1044
1099
 
1100
+ /**
1101
+ * Add a block received from sync (RPC fallback or P2P)
1102
+ * Validates the block before adding to chain
1103
+ */
1104
+ async addBlock(block) {
1105
+ if (!block || !block.number) {
1106
+ throw new Error('Invalid block');
1107
+ }
1108
+
1109
+ const expectedNumber = this.chain.length;
1110
+ if (block.number !== expectedNumber) {
1111
+ // Skip if we already have this block or it's out of order
1112
+ if (block.number < expectedNumber) {
1113
+ return { status: 'skipped', reason: 'already_have' };
1114
+ }
1115
+ throw new Error(`Block out of order: expected ${expectedNumber}, got ${block.number}`);
1116
+ }
1117
+
1118
+ // Validate parent hash
1119
+ const parentBlock = this.getLatestBlock();
1120
+ if (block.parentHash && parentBlock.hash && block.parentHash !== parentBlock.hash) {
1121
+ throw new Error('Invalid parent hash - possible chain reorg');
1122
+ }
1123
+
1124
+ // Add block to chain
1125
+ this.chain.push(block);
1126
+
1127
+ // Process transactions in the block
1128
+ if (block.transactions && Array.isArray(block.transactions)) {
1129
+ for (const tx of block.transactions) {
1130
+ // Update account states from transactions
1131
+ if (tx.from && tx.to && tx.value) {
1132
+ const value = BigInt(tx.value || '0');
1133
+ const fromBalance = BigInt(this.state.get(tx.from)?.balance || '0');
1134
+ const toBalance = BigInt(this.state.get(tx.to)?.balance || '0');
1135
+
1136
+ // Debit sender, credit receiver
1137
+ this.state.set(tx.from, {
1138
+ ...this.state.get(tx.from),
1139
+ balance: (fromBalance - value).toString(),
1140
+ nonce: (parseInt(this.state.get(tx.from)?.nonce || '0') + 1).toString()
1141
+ });
1142
+ this.state.set(tx.to, {
1143
+ ...this.state.get(tx.to),
1144
+ balance: (toBalance + value).toString()
1145
+ });
1146
+ }
1147
+ }
1148
+ }
1149
+
1150
+ console.log(`[BLOCKCHAIN] Synced block #${block.number} (${block.transactions?.length || 0} txs)`);
1151
+
1152
+ return { status: 'added', blockNumber: block.number };
1153
+ }
1154
+
1155
+ /**
1156
+ * Get sync status (jaelis_syncing compatible)
1157
+ */
1158
+ getSyncStatus(remoteHeight) {
1159
+ const localHeight = this.getHeight();
1160
+ const isSyncing = localHeight < remoteHeight;
1161
+
1162
+ if (!isSyncing) {
1163
+ return false; // Fully synced
1164
+ }
1165
+
1166
+ return {
1167
+ startingBlock: '0x0',
1168
+ currentBlock: '0x' + localHeight.toString(16),
1169
+ highestBlock: '0x' + remoteHeight.toString(16),
1170
+ syncedBlocks: localHeight,
1171
+ remainingBlocks: remoteHeight - localHeight,
1172
+ percentComplete: ((localHeight / remoteHeight) * 100).toFixed(2) + '%'
1173
+ };
1174
+ }
1175
+
1045
1176
  async save() {
1046
1177
  // Save blockchain state to disk
1047
1178
  // TODO: Implement persistent storage
@@ -1125,30 +1256,113 @@ class EmbeddedNetwork {
1125
1256
  }
1126
1257
 
1127
1258
  async _startRPCFallback() {
1128
- // Sync from remote RPC periodically
1129
- const syncFromRPC = async () => {
1130
- try {
1259
+ // Track sync state
1260
+ this.syncState = {
1261
+ localHeight: 0,
1262
+ remoteHeight: 0,
1263
+ syncing: false,
1264
+ lastSync: null,
1265
+ blocksDownloaded: 0
1266
+ };
1267
+
1268
+ // Helper to make RPC calls to remote node
1269
+ const rpcCall = async (method, params = []) => {
1270
+ return new Promise((resolve, reject) => {
1131
1271
  const https = require('https');
1132
1272
  const http = require('http');
1133
- const protocol = this.remoteRpc.startsWith('https') ? https : http;
1273
+ const url = new URL(this.remoteRpc);
1274
+ const protocol = url.protocol === 'https:' ? https : http;
1134
1275
 
1135
1276
  const data = JSON.stringify({
1136
1277
  jsonrpc: '2.0',
1137
- method: 'eth_blockNumber',
1138
- params: [],
1139
- id: 1
1278
+ method,
1279
+ params,
1280
+ id: Date.now()
1281
+ });
1282
+
1283
+ const req = protocol.request({
1284
+ hostname: url.hostname,
1285
+ port: url.port || (url.protocol === 'https:' ? 443 : 80),
1286
+ path: url.pathname || '/',
1287
+ method: 'POST',
1288
+ headers: {
1289
+ 'Content-Type': 'application/json',
1290
+ 'Content-Length': Buffer.byteLength(data)
1291
+ }
1292
+ }, (res) => {
1293
+ let body = '';
1294
+ res.on('data', chunk => body += chunk);
1295
+ res.on('end', () => {
1296
+ try {
1297
+ const json = JSON.parse(body);
1298
+ if (json.error) reject(new Error(json.error.message));
1299
+ else resolve(json.result);
1300
+ } catch (e) { reject(e); }
1301
+ });
1140
1302
  });
1141
1303
 
1142
- // Simple fetch for block number (keeps node in sync)
1143
- // Full sync implementation would fetch blocks
1304
+ req.on('error', reject);
1305
+ req.write(data);
1306
+ req.end();
1307
+ });
1308
+ };
1309
+
1310
+ // Sync blocks from remote RPC using JAELIS NATIVE methods
1311
+ const syncFromRPC = async () => {
1312
+ if (this.syncState.syncing) return; // Already syncing
1313
+ this.syncState.syncing = true;
1314
+
1315
+ try {
1316
+ // 1. Get remote block height using NATIVE jaelis_blockNumber
1317
+ const remoteHeightHex = await rpcCall('jaelis_blockNumber');
1318
+ this.syncState.remoteHeight = parseInt(remoteHeightHex, 16);
1319
+
1320
+ // 2. Get local block height
1321
+ this.syncState.localHeight = this.blockchain?.getHeight?.() || 0;
1322
+
1323
+ // 3. Sync missing blocks (batch of 10 at a time)
1324
+ const blocksToSync = Math.min(10, this.syncState.remoteHeight - this.syncState.localHeight);
1325
+
1326
+ if (blocksToSync > 0) {
1327
+ console.log(`[SYNC] Syncing blocks ${this.syncState.localHeight + 1} to ${this.syncState.localHeight + blocksToSync} (remote: ${this.syncState.remoteHeight})`);
1328
+
1329
+ for (let i = 1; i <= blocksToSync; i++) {
1330
+ const blockNum = this.syncState.localHeight + i;
1331
+ const blockHex = '0x' + blockNum.toString(16);
1332
+
1333
+ // Fetch full block with transactions using NATIVE jaelis_getBlockByNumber
1334
+ // This returns JAELIS-native fields (lodeUsed, consensus: PoEC, etc.)
1335
+ const block = await rpcCall('jaelis_getBlockByNumber', [blockHex, true]);
1336
+
1337
+ if (block) {
1338
+ // Store block in local blockchain
1339
+ if (this.blockchain?.addBlock) {
1340
+ await this.blockchain.addBlock(block);
1341
+ }
1342
+ this.syncState.blocksDownloaded++;
1343
+ }
1344
+ }
1345
+
1346
+ this.syncState.localHeight += blocksToSync;
1347
+ console.log(`[SYNC] Downloaded ${blocksToSync} blocks. Local height: ${this.syncState.localHeight}`);
1348
+ }
1349
+
1350
+ this.syncState.lastSync = Date.now();
1351
+ this.emit('sync', this.syncState);
1352
+
1144
1353
  } catch (err) {
1145
- // Silent fail - will retry
1354
+ console.error('[SYNC] Error:', err.message);
1355
+ } finally {
1356
+ this.syncState.syncing = false;
1146
1357
  }
1147
1358
  };
1148
1359
 
1149
- // Sync every 5 seconds
1150
- this.syncInterval = setInterval(syncFromRPC, 5000);
1360
+ // Initial sync
1361
+ console.log('[SYNC] Starting RPC fallback sync from', this.remoteRpc);
1151
1362
  await syncFromRPC();
1363
+
1364
+ // Continue syncing every 3 seconds (JAELIS has 3-second blocks)
1365
+ this.syncInterval = setInterval(syncFromRPC, 3000);
1152
1366
  }
1153
1367
 
1154
1368
  async stop() {
@@ -1172,9 +1386,10 @@ class EmbeddedNetwork {
1172
1386
  }
1173
1387
 
1174
1388
  class EmbeddedRpcServer {
1175
- constructor(blockchain, port) {
1389
+ constructor(blockchain, port, network = null) {
1176
1390
  this.blockchain = blockchain;
1177
1391
  this.port = port;
1392
+ this.network = network; // Reference to EmbeddedNetwork for sync status
1178
1393
  this.app = null;
1179
1394
  }
1180
1395
 
@@ -1242,6 +1457,36 @@ class EmbeddedRpcServer {
1242
1457
  case 'jaelis_blockNumber':
1243
1458
  return '0x' + (this.blockchain?.getHeight?.() || 0).toString(16);
1244
1459
 
1460
+ case 'jaelis_syncing':
1461
+ // Return sync status - false if fully synced, object if syncing
1462
+ if (this.network?.syncState) {
1463
+ return this.blockchain?.getSyncStatus?.(this.network.syncState.remoteHeight) || false;
1464
+ }
1465
+ return false;
1466
+
1467
+ case 'jaelis_getBlockByNumber':
1468
+ // Native JAELIS method - returns JAELIS-native block fields
1469
+ const jaelisBlockNum = typeof params[0] === 'string' && params[0].startsWith('0x')
1470
+ ? parseInt(params[0], 16)
1471
+ : params[0];
1472
+ const jaelisBlock = this.blockchain?.getBlock?.(jaelisBlockNum);
1473
+ if (jaelisBlock) {
1474
+ // Add JAELIS-specific fields
1475
+ return {
1476
+ ...jaelisBlock,
1477
+ lodeUsed: '0x0', // ZERO FEES
1478
+ consensus: 'PoEC', // Proof of Entropy Contribution
1479
+ jaelisVersion: '1.0.0'
1480
+ };
1481
+ }
1482
+ return null;
1483
+
1484
+ case 'jaelis_getBlockByHash':
1485
+ // Find block by hash
1486
+ const targetHash = params[0];
1487
+ const foundBlock = this.blockchain?.chain?.find(b => b.hash === targetHash);
1488
+ return foundBlock || null;
1489
+
1245
1490
  // ═══════════════════════════════════════════════════════════════
1246
1491
  // LAYER 2: ETHEREUM COMPATIBILITY (PROXIES → jaelis_*)
1247
1492
  // ═══════════════════════════════════════════════════════════════
@@ -1265,9 +1510,35 @@ class EmbeddedRpcServer {
1265
1510
  case 'eth_getTransactionReceipt':
1266
1511
  return this.blockchain?.vmExecutor?.getReceipt?.(params[0]) || null;
1267
1512
 
1513
+ case 'eth_getCode':
1514
+ // CRITICAL: This is how block explorers detect contracts!
1515
+ // Returns bytecode at address, or '0x' for EOAs
1516
+ return this._getCode(params[0], params[1] || 'latest');
1517
+
1518
+ case 'eth_getBalance':
1519
+ return this._getBalance(params[0], params[1] || 'latest');
1520
+
1521
+ case 'eth_getTransactionCount':
1522
+ return this._getTransactionCount(params[0], params[1] || 'latest');
1523
+
1524
+ case 'eth_getStorageAt':
1525
+ return this._getStorageAt(params[0], params[1], params[2] || 'latest');
1526
+
1527
+ case 'eth_estimateGas':
1528
+ // JAELIS has ZERO GAS - always return minimal
1529
+ return '0x5208'; // 21000 in hex
1530
+
1531
+ case 'eth_gasPrice':
1532
+ // JAELIS has ZERO GAS
1533
+ return '0x0';
1534
+
1268
1535
  // ═══════════════════════════════════════════════════════════════
1269
1536
  // JAELIS-SPECIFIC METHODS (Unified VM)
1270
1537
  // ═══════════════════════════════════════════════════════════════
1538
+ case 'jaelis_getCode':
1539
+ // Native JAELIS method - returns contract bytecode
1540
+ return this._getCode(params[0], params[1] || 'latest');
1541
+
1271
1542
  case 'jaelis_getHealth':
1272
1543
  return {
1273
1544
  status: 'healthy',
@@ -1276,11 +1547,18 @@ class EmbeddedRpcServer {
1276
1547
  vmActive: !!this.blockchain?.vmExecutor
1277
1548
  };
1278
1549
 
1550
+ case 'jaelis_deploy':
1279
1551
  case 'jaelis_deployContract':
1280
1552
  // Deploy contract in ANY language!
1281
1553
  // params: [source, language, options]
1282
1554
  return this._deployContract(params[0], params[1], params[2] || {});
1283
1555
 
1556
+ case 'jaelis_deployUnified':
1557
+ // THE KEY INNOVATION: Deploy unified contract bundle!
1558
+ // Compile MULTIPLE contracts from MULTIPLE languages into ONE bytecode!
1559
+ // params: [{ from, sources: [{ code, language, name }...] }]
1560
+ return this._deployUnified(params[0]);
1561
+
1284
1562
  case 'jaelis_executeContract':
1285
1563
  // Execute contract call
1286
1564
  // params: [to, functionName, args, options]
@@ -1300,6 +1578,45 @@ class EmbeddedRpcServer {
1300
1578
  case 'jaelis_getSupportedLanguages':
1301
1579
  return ['solidity', 'rust', 'move', 'func', 'cairo', 'vyper'];
1302
1580
 
1581
+ // ═══════════════════════════════════════════════════════════════
1582
+ // CROSS-CHAIN DEPLOYMENT METHODS (NEW!)
1583
+ // Query contract deployments across chains
1584
+ // ═══════════════════════════════════════════════════════════════
1585
+
1586
+ case 'jaelis_crossChain_getDeployment':
1587
+ // Get deployment info for an address
1588
+ // params: [address]
1589
+ return this._getCrossChainDeployment(params[0]);
1590
+
1591
+ case 'jaelis_crossChain_getChainDeployments':
1592
+ // Get all deployments to a specific chain
1593
+ // params: [chainId]
1594
+ return this._getChainDeployments(params[0]);
1595
+
1596
+ case 'jaelis_crossChain_getExplorerUrl':
1597
+ // Get explorer URL for a deployment
1598
+ // params: [chainId, address]
1599
+ return this._getExplorerUrl(params[0], params[1]);
1600
+
1601
+ case 'jaelis_crossChain_getDeploymentStats':
1602
+ // Get deployment statistics
1603
+ return this._getDeploymentStats();
1604
+
1605
+ // ═══════════════════════════════════════════════════════════════
1606
+ // MULTI-CHAIN SIGNATURE METHODS (Per-chain wallet authorization)
1607
+ // ═══════════════════════════════════════════════════════════════
1608
+
1609
+ case 'jaelis_crossChain_getSigningRequests':
1610
+ // Get signing messages for each target chain
1611
+ // params: [bytecode, targetChains[]]
1612
+ // Returns array of { chainId, message, switchParams } for frontend
1613
+ return this._getMultiChainSigningRequests(params[0], params[1]);
1614
+
1615
+ case 'jaelis_crossChain_deployWithSignatures':
1616
+ // Deploy with per-chain wallet signatures
1617
+ // params: [{ from, bytecode, abi, targetChains, chainSignatures: {chainId: sig} }]
1618
+ return this._deployWithMultiChainSignatures(params[0]);
1619
+
1303
1620
  // ═══════════════════════════════════════════════════════════════
1304
1621
  // CROSS-CHAIN SETTLEMENT METHODS (Universal State Layer!)
1305
1622
  // ═══════════════════════════════════════════════════════════════
@@ -1345,6 +1662,43 @@ class EmbeddedRpcServer {
1345
1662
  // Get cross-chain stats
1346
1663
  return this._getCrossChainStats();
1347
1664
 
1665
+ // ═══════════════════════════════════════════════════════════════
1666
+ // MCP SHORTHAND ALIASES (crosschain_* → jaelis_crossChain_*)
1667
+ // Unified RPC - same methods, shorter names for AI agents
1668
+ // ═══════════════════════════════════════════════════════════════
1669
+ case 'crosschain_getChains':
1670
+ return this._handleMethod('jaelis_crossChain_getChains', params);
1671
+ case 'crosschain_getChainInfo':
1672
+ return this._handleMethod('jaelis_crossChain_getChainInfo', params);
1673
+ case 'crosschain_readState':
1674
+ return this._handleMethod('jaelis_crossChain_readState', params);
1675
+ case 'crosschain_settleState':
1676
+ return this._handleMethod('jaelis_crossChain_settleState', params);
1677
+ case 'crosschain_getStateDiff':
1678
+ return this._handleMethod('jaelis_crossChain_getStateDiff', params);
1679
+ case 'crosschain_computeSlot':
1680
+ return this._handleMethod('jaelis_crossChain_computeSlot', params);
1681
+ case 'crosschain_getLightClientStatus':
1682
+ return this._handleMethod('jaelis_crossChain_getLightClientStatus', params);
1683
+ case 'crosschain_getSettlements':
1684
+ return this._handleMethod('jaelis_crossChain_getSettlements', params);
1685
+ case 'crosschain_getStats':
1686
+ return this._handleMethod('jaelis_crossChain_getStats', params);
1687
+ case 'crosschain_getDeployment':
1688
+ return this._handleMethod('jaelis_crossChain_getDeployment', params);
1689
+ case 'crosschain_getExplorerUrl':
1690
+ return this._handleMethod('jaelis_crossChain_getExplorerUrl', params);
1691
+ case 'crosschain_getDeploymentStats':
1692
+ return this._handleMethod('jaelis_crossChain_getDeploymentStats', params);
1693
+ case 'crosschain_getSigningRequests':
1694
+ return this._handleMethod('jaelis_crossChain_getSigningRequests', params);
1695
+ case 'crosschain_deployWithSignatures':
1696
+ return this._handleMethod('jaelis_crossChain_deployWithSignatures', params);
1697
+ case 'crosschain_deployUnified':
1698
+ return this._handleMethod('jaelis_deployUnified', params);
1699
+ case 'crosschain_syncState':
1700
+ return this._handleMethod('jaelis_crossChain_settleState', params);
1701
+
1348
1702
  // ═══════════════════════════════════════════════════════════════
1349
1703
  // LAYER 2: SOLANA COMPATIBILITY (PROXIES → jaelis_*)
1350
1704
  // For Rust/SPL developers familiar with Solana RPC
@@ -1567,6 +1921,160 @@ class EmbeddedRpcServer {
1567
1921
  return result.transactionHash || '0x0';
1568
1922
  }
1569
1923
 
1924
+ /**
1925
+ * Get contract bytecode at address
1926
+ *
1927
+ * CRITICAL: This is how block explorers detect contracts!
1928
+ * - Returns bytecode hex string if contract exists
1929
+ * - Returns '0x' if address is an EOA or doesn't exist
1930
+ *
1931
+ * Block explorers use: eth_getCode(address) !== '0x' → it's a contract!
1932
+ */
1933
+ async _getCode(address, blockTag = 'latest') {
1934
+ if (!this.blockchain) return '0x';
1935
+
1936
+ try {
1937
+ // Normalize address
1938
+ const normalizedAddr = address.toLowerCase();
1939
+
1940
+ // Try StateManager first (persistent LevelDB storage)
1941
+ if (this.blockchain.stateManager) {
1942
+ const code = await this.blockchain.stateManager.getCode(normalizedAddr);
1943
+ if (code && code.length > 0) {
1944
+ // Return as hex string
1945
+ if (Buffer.isBuffer(code)) {
1946
+ return '0x' + code.toString('hex');
1947
+ }
1948
+ if (typeof code === 'string') {
1949
+ return code.startsWith('0x') ? code : '0x' + code;
1950
+ }
1951
+ return '0x' + Buffer.from(code).toString('hex');
1952
+ }
1953
+ }
1954
+
1955
+ // Try VM Executor
1956
+ if (this.blockchain.vmExecutor?.stateManager) {
1957
+ const code = await this.blockchain.vmExecutor.stateManager.getCode(normalizedAddr);
1958
+ if (code && code.length > 0) {
1959
+ if (Buffer.isBuffer(code)) {
1960
+ return '0x' + code.toString('hex');
1961
+ }
1962
+ return code.startsWith('0x') ? code : '0x' + code;
1963
+ }
1964
+ }
1965
+
1966
+ // Try UnifiedVM contracts
1967
+ if (this.blockchain.unifiedVM?.contractManager) {
1968
+ const contract = this.blockchain.unifiedVM.contractManager.contracts.get(normalizedAddr);
1969
+ if (contract?.bytecode) {
1970
+ if (Buffer.isBuffer(contract.bytecode)) {
1971
+ return '0x' + contract.bytecode.toString('hex');
1972
+ }
1973
+ return contract.bytecode.startsWith('0x') ? contract.bytecode : '0x' + contract.bytecode;
1974
+ }
1975
+ }
1976
+
1977
+ // Try contracts Map directly
1978
+ if (this.blockchain.contracts) {
1979
+ const contract = this.blockchain.contracts.get(normalizedAddr);
1980
+ if (contract?.bytecode) {
1981
+ if (Buffer.isBuffer(contract.bytecode)) {
1982
+ return '0x' + contract.bytecode.toString('hex');
1983
+ }
1984
+ return contract.bytecode.startsWith('0x') ? contract.bytecode : '0x' + contract.bytecode;
1985
+ }
1986
+ }
1987
+
1988
+ // No bytecode found - it's an EOA or doesn't exist
1989
+ return '0x';
1990
+ } catch (error) {
1991
+ console.error(`[RPC] _getCode error for ${address}:`, error.message);
1992
+ return '0x';
1993
+ }
1994
+ }
1995
+
1996
+ /**
1997
+ * Get account balance
1998
+ */
1999
+ async _getBalance(address, blockTag = 'latest') {
2000
+ if (!this.blockchain) return '0x0';
2001
+
2002
+ try {
2003
+ const normalizedAddr = address.toLowerCase();
2004
+
2005
+ // Try StateManager
2006
+ if (this.blockchain.stateManager) {
2007
+ const account = await this.blockchain.stateManager.getAccount(normalizedAddr);
2008
+ if (account?.balance !== undefined) {
2009
+ const balance = BigInt(account.balance || 0);
2010
+ return '0x' + balance.toString(16);
2011
+ }
2012
+ }
2013
+
2014
+ // Default balance (JAELIS has no native token, but return 0)
2015
+ return '0x0';
2016
+ } catch (error) {
2017
+ return '0x0';
2018
+ }
2019
+ }
2020
+
2021
+ /**
2022
+ * Get transaction count (nonce)
2023
+ */
2024
+ async _getTransactionCount(address, blockTag = 'latest') {
2025
+ if (!this.blockchain) return '0x0';
2026
+
2027
+ try {
2028
+ const normalizedAddr = address.toLowerCase();
2029
+
2030
+ // Try StateManager
2031
+ if (this.blockchain.stateManager) {
2032
+ const account = await this.blockchain.stateManager.getAccount(normalizedAddr);
2033
+ if (account?.nonce !== undefined) {
2034
+ const nonce = BigInt(account.nonce || 0);
2035
+ return '0x' + nonce.toString(16);
2036
+ }
2037
+ }
2038
+
2039
+ return '0x0';
2040
+ } catch (error) {
2041
+ return '0x0';
2042
+ }
2043
+ }
2044
+
2045
+ /**
2046
+ * Get storage value at slot
2047
+ */
2048
+ async _getStorageAt(address, slot, blockTag = 'latest') {
2049
+ if (!this.blockchain) return '0x' + '0'.repeat(64);
2050
+
2051
+ try {
2052
+ const normalizedAddr = address.toLowerCase();
2053
+
2054
+ // Try StateManager
2055
+ if (this.blockchain.stateManager) {
2056
+ const value = await this.blockchain.stateManager.getStorage(normalizedAddr, slot);
2057
+ if (value !== undefined) {
2058
+ const bigValue = BigInt(value || 0);
2059
+ return '0x' + bigValue.toString(16).padStart(64, '0');
2060
+ }
2061
+ }
2062
+
2063
+ // Try VM Executor StateManager
2064
+ if (this.blockchain.vmExecutor?.stateManager) {
2065
+ const value = await this.blockchain.vmExecutor.stateManager.getStorage(normalizedAddr, slot);
2066
+ if (value !== undefined) {
2067
+ const bigValue = BigInt(value || 0);
2068
+ return '0x' + bigValue.toString(16).padStart(64, '0');
2069
+ }
2070
+ }
2071
+
2072
+ return '0x' + '0'.repeat(64);
2073
+ } catch (error) {
2074
+ return '0x' + '0'.repeat(64);
2075
+ }
2076
+ }
2077
+
1570
2078
  async _call(callObject) {
1571
2079
  if (!this.blockchain?.vmExecutor) throw new Error('VM not available');
1572
2080
  const result = await this.blockchain.vmExecutor.executeCall(
@@ -1589,6 +2097,150 @@ class EmbeddedRpcServer {
1589
2097
  };
1590
2098
  }
1591
2099
 
2100
+ /**
2101
+ * Deploy unified contract bundle (THE KEY INNOVATION!)
2102
+ *
2103
+ * Compiles MULTIPLE contracts from MULTIPLE languages into ONE bytecode bundle.
2104
+ * All contracts share memory and can call each other DIRECTLY - no bridges!
2105
+ *
2106
+ * NOW WITH CROSS-CHAIN PROPAGATION:
2107
+ * - Contracts appear on ETH, SOL, and other block explorers!
2108
+ * - Shadow contracts on external chains proxy to JAELIS
2109
+ *
2110
+ * @param {Object} params - { from, sources: [{ code, language, name }...], targetChains?: number[] }
2111
+ */
2112
+ async _deployUnified(params) {
2113
+ if (!this.blockchain) throw new Error('Blockchain not available');
2114
+ if (!this.blockchain.unifiedVM) throw new Error('Unified VM not available');
2115
+
2116
+ const { from, sources, targetChains } = params;
2117
+
2118
+ if (!sources || !Array.isArray(sources) || sources.length === 0) {
2119
+ throw new Error('sources array is required for unified deployment');
2120
+ }
2121
+
2122
+ console.log(`[RPC] Deploying unified bundle with ${sources.length} contracts...`);
2123
+ console.log(`[RPC] Languages: ${[...new Set(sources.map(s => s.language))].join(', ')}`);
2124
+
2125
+ // Default target chains if not specified - ALL 30+ chains we support!
2126
+ const defaultTargetChains = [
2127
+ // EVM Mainnets
2128
+ 1, // Ethereum Mainnet
2129
+ 137, // Polygon PoS
2130
+ 42161, // Arbitrum One
2131
+ 42170, // Arbitrum Nova
2132
+ 10, // OP Mainnet
2133
+ 8453, // Base
2134
+ 56, // BNB Smart Chain
2135
+ 43114, // Avalanche C-Chain
2136
+ 250, // Fantom Opera
2137
+ 100, // Gnosis
2138
+ 59144, // Linea
2139
+ 324, // zkSync Era
2140
+ 534352, // Scroll
2141
+ 5000, // Mantle
2142
+ 81457, // Blast
2143
+
2144
+ // Solana
2145
+ 101, // Solana Mainnet
2146
+
2147
+ // Move chains (using type prefix to differentiate)
2148
+ // Note: Aptos/Sui use same IDs but different networks
2149
+
2150
+ // TON
2151
+ -1, // TON Mainnet
2152
+
2153
+ // StarkNet (using string identifier due to large chain ID)
2154
+ 'starknet-main', // StarkNet Mainnet
2155
+
2156
+ // Cosmos
2157
+ 118, // Cosmos Hub
2158
+ 119, // Osmosis
2159
+
2160
+ // Bitcoin
2161
+ 0 // Bitcoin Mainnet
2162
+ ];
2163
+ const chains = targetChains || defaultTargetChains;
2164
+
2165
+ console.log(`[RPC] Target chains: ${chains.join(', ')}`);
2166
+
2167
+ // Use the new deployUnifiedWithPropagation for cross-chain deployment
2168
+ if (this.blockchain.unifiedVM.deployUnifiedWithPropagation) {
2169
+ const result = await this.blockchain.unifiedVM.deployUnifiedWithPropagation({
2170
+ from,
2171
+ sources,
2172
+ targetChains: chains
2173
+ });
2174
+
2175
+ // Build contracts map for response
2176
+ const contractsMap = {};
2177
+ for (const source of sources) {
2178
+ contractsMap[source.name] = {
2179
+ address: result.address,
2180
+ language: source.language,
2181
+ name: source.name
2182
+ };
2183
+ }
2184
+
2185
+ console.log(`[RPC] Unified bundle deployed at ${result.address}`);
2186
+ console.log(`[RPC] Chain deployments: ${result.deployments?.length || 0}`);
2187
+
2188
+ // Log explorer URLs
2189
+ if (result.explorerUrls) {
2190
+ console.log(`[RPC] Explorer URLs:`);
2191
+ for (const [chainId, url] of Object.entries(result.explorerUrls)) {
2192
+ console.log(`[RPC] Chain ${chainId}: ${url}`);
2193
+ }
2194
+ }
2195
+
2196
+ return {
2197
+ address: result.address,
2198
+ bytecode: result.bytecode,
2199
+ abi: result.abi || [],
2200
+ contracts: contractsMap,
2201
+ deployments: result.deployments,
2202
+ explorerUrls: result.explorerUrls,
2203
+ metadata: result.metadata,
2204
+ gasUsed: 0 // ZERO FEES!
2205
+ };
2206
+ }
2207
+
2208
+ // Fallback: original local-only deployment
2209
+ console.log(`[RPC] Fallback: local-only deployment (no cross-chain propagation)`);
2210
+
2211
+ const compiled = await this.blockchain.unifiedVM.compileBundle(sources);
2212
+ const address = '0x' + require('crypto').randomBytes(20).toString('hex');
2213
+
2214
+ if (this.blockchain.contracts) {
2215
+ this.blockchain.contracts.set(address, {
2216
+ bytecode: compiled.bytecode,
2217
+ abi: compiled.abi || [],
2218
+ sources: sources.map(s => s.name),
2219
+ languages: [...new Set(sources.map(s => s.language))],
2220
+ deployedAt: Date.now(),
2221
+ deployer: from
2222
+ });
2223
+ }
2224
+
2225
+ const contractsMap = {};
2226
+ for (const source of sources) {
2227
+ contractsMap[source.name] = {
2228
+ address: address,
2229
+ language: source.language,
2230
+ name: source.name
2231
+ };
2232
+ }
2233
+
2234
+ return {
2235
+ address,
2236
+ bytecode: compiled.bytecode?.toString('hex'),
2237
+ abi: compiled.abi || [],
2238
+ contracts: contractsMap,
2239
+ metadata: compiled.metadata,
2240
+ gasUsed: 0 // ZERO FEES!
2241
+ };
2242
+ }
2243
+
1592
2244
  async _executeContract(to, functionName, args, options) {
1593
2245
  if (!this.blockchain) throw new Error('Blockchain not available');
1594
2246
  return this.blockchain.executeContract(to, functionName, args, options);
@@ -1728,6 +2380,131 @@ class EmbeddedRpcServer {
1728
2380
  };
1729
2381
  }
1730
2382
 
2383
+ // ═══════════════════════════════════════════════════════════════
2384
+ // CROSS-CHAIN DEPLOYMENT QUERY IMPLEMENTATIONS
2385
+ // ═══════════════════════════════════════════════════════════════
2386
+
2387
+ _getCrossChainDeployment(address) {
2388
+ if (this.blockchain?.unifiedVM?.deployManager?.getDeployment) {
2389
+ const deployment = this.blockchain.unifiedVM.deployManager.getDeployment(address);
2390
+ if (deployment) {
2391
+ return deployment;
2392
+ }
2393
+ }
2394
+ return { error: 'Deployment not found', address };
2395
+ }
2396
+
2397
+ _getChainDeployments(chainId) {
2398
+ if (this.blockchain?.unifiedVM?.deployManager?.getChainDeployments) {
2399
+ return this.blockchain.unifiedVM.deployManager.getChainDeployments(chainId);
2400
+ }
2401
+ return [];
2402
+ }
2403
+
2404
+ _getExplorerUrl(chainId, address) {
2405
+ if (this.blockchain?.unifiedVM?.deployManager?.getExplorerUrl) {
2406
+ const url = this.blockchain.unifiedVM.deployManager.getExplorerUrl(chainId, address);
2407
+ return { chainId, address, url };
2408
+ }
2409
+ return { chainId, address, url: null, error: 'Deploy manager not available' };
2410
+ }
2411
+
2412
+ _getDeploymentStats() {
2413
+ if (this.blockchain?.unifiedVM?.deployManager?.getStats) {
2414
+ return this.blockchain.unifiedVM.deployManager.getStats();
2415
+ }
2416
+ return {
2417
+ totalDeployments: 0,
2418
+ pendingCount: 0,
2419
+ failedCount: 0,
2420
+ chainStats: {}
2421
+ };
2422
+ }
2423
+
2424
+ // ═══════════════════════════════════════════════════════════════
2425
+ // MULTI-CHAIN SIGNATURE IMPLEMENTATIONS
2426
+ // For external chains to accept deployments, user signs per-chain
2427
+ // ═══════════════════════════════════════════════════════════════
2428
+
2429
+ /**
2430
+ * Get signing requests for each target chain
2431
+ *
2432
+ * Frontend flow:
2433
+ * 1. Get these messages
2434
+ * 2. For each chain, switch wallet network and sign
2435
+ * 3. Collect all signatures
2436
+ * 4. Call deployWithSignatures
2437
+ */
2438
+ _getMultiChainSigningRequests(bytecode, targetChains) {
2439
+ if (this.blockchain?.unifiedVM?.deployManager?.getMultiChainSigningRequests) {
2440
+ return this.blockchain.unifiedVM.deployManager.getMultiChainSigningRequests(bytecode, targetChains);
2441
+ }
2442
+
2443
+ // Fallback implementation
2444
+ const crypto = require('crypto');
2445
+ const bytecodeHash = crypto.createHash('sha256')
2446
+ .update(bytecode || '')
2447
+ .digest('hex')
2448
+ .substring(0, 16);
2449
+
2450
+ const timestamp = Date.now();
2451
+ const requests = [];
2452
+
2453
+ for (const chainId of (targetChains || [])) {
2454
+ const message = `JAELIS Cross-Chain Deploy Authorization
2455
+
2456
+ Chain ID: ${chainId}
2457
+ Bytecode Hash: ${bytecodeHash}
2458
+ Timestamp: ${timestamp}
2459
+
2460
+ I authorize JAELIS to deploy this contract.
2461
+ ZERO FEES - No gas cost to me.`;
2462
+
2463
+ requests.push({
2464
+ chainId,
2465
+ message,
2466
+ messageHash: '0x' + crypto.createHash('sha256')
2467
+ .update(`\x19Ethereum Signed Message:\n${message.length}${message}`)
2468
+ .digest('hex'),
2469
+ switchParams: { chainId: '0x' + chainId.toString(16) }
2470
+ });
2471
+ }
2472
+
2473
+ return { requests, bytecodeHash, timestamp, totalChains: targetChains?.length || 0 };
2474
+ }
2475
+
2476
+ /**
2477
+ * Deploy with per-chain wallet signatures
2478
+ *
2479
+ * Each chain that needs to recognize the deployment
2480
+ * requires its own signature from the deployer's wallet.
2481
+ */
2482
+ async _deployWithMultiChainSignatures(params) {
2483
+ const { from, bytecode, abi, targetChains, chainSignatures, metadata } = params;
2484
+
2485
+ console.log(`[RPC] Multi-chain signature deployment from ${from}`);
2486
+ console.log(`[RPC] Target chains: ${targetChains?.length || 0}`);
2487
+ console.log(`[RPC] Signatures provided: ${Object.keys(chainSignatures || {}).length}`);
2488
+
2489
+ if (this.blockchain?.unifiedVM?.deployManager?.deployWithMultiChainSignatures) {
2490
+ return await this.blockchain.unifiedVM.deployManager.deployWithMultiChainSignatures({
2491
+ from,
2492
+ bytecode,
2493
+ abi,
2494
+ targetChains,
2495
+ chainSignatures,
2496
+ metadata
2497
+ });
2498
+ }
2499
+
2500
+ // Fallback: regular deployment without chain signatures
2501
+ console.log(`[RPC] Fallback: Deploy manager not available, using standard flow`);
2502
+ return {
2503
+ error: 'Multi-chain signatures not supported',
2504
+ message: 'Deploy manager not initialized'
2505
+ };
2506
+ }
2507
+
1731
2508
  async stop() {
1732
2509
  if (this.server) {
1733
2510
  this.server.close();