jaelis-node 1.5.0 → 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',
@@ -1307,6 +1578,45 @@ class EmbeddedRpcServer {
1307
1578
  case 'jaelis_getSupportedLanguages':
1308
1579
  return ['solidity', 'rust', 'move', 'func', 'cairo', 'vyper'];
1309
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
+
1310
1620
  // ═══════════════════════════════════════════════════════════════
1311
1621
  // CROSS-CHAIN SETTLEMENT METHODS (Universal State Layer!)
1312
1622
  // ═══════════════════════════════════════════════════════════════
@@ -1352,6 +1662,43 @@ class EmbeddedRpcServer {
1352
1662
  // Get cross-chain stats
1353
1663
  return this._getCrossChainStats();
1354
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
+
1355
1702
  // ═══════════════════════════════════════════════════════════════
1356
1703
  // LAYER 2: SOLANA COMPATIBILITY (PROXIES → jaelis_*)
1357
1704
  // For Rust/SPL developers familiar with Solana RPC
@@ -1574,6 +1921,160 @@ class EmbeddedRpcServer {
1574
1921
  return result.transactionHash || '0x0';
1575
1922
  }
1576
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
+
1577
2078
  async _call(callObject) {
1578
2079
  if (!this.blockchain?.vmExecutor) throw new Error('VM not available');
1579
2080
  const result = await this.blockchain.vmExecutor.executeCall(
@@ -1602,13 +2103,17 @@ class EmbeddedRpcServer {
1602
2103
  * Compiles MULTIPLE contracts from MULTIPLE languages into ONE bytecode bundle.
1603
2104
  * All contracts share memory and can call each other DIRECTLY - no bridges!
1604
2105
  *
1605
- * @param {Object} params - { from, sources: [{ code, language, name }...] }
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[] }
1606
2111
  */
1607
2112
  async _deployUnified(params) {
1608
2113
  if (!this.blockchain) throw new Error('Blockchain not available');
1609
2114
  if (!this.blockchain.unifiedVM) throw new Error('Unified VM not available');
1610
2115
 
1611
- const { from, sources } = params;
2116
+ const { from, sources, targetChains } = params;
1612
2117
 
1613
2118
  if (!sources || !Array.isArray(sources) || sources.length === 0) {
1614
2119
  throw new Error('sources array is required for unified deployment');
@@ -1617,13 +2122,95 @@ class EmbeddedRpcServer {
1617
2122
  console.log(`[RPC] Deploying unified bundle with ${sources.length} contracts...`);
1618
2123
  console.log(`[RPC] Languages: ${[...new Set(sources.map(s => s.language))].join(', ')}`);
1619
2124
 
1620
- // Compile all sources together using the Unified Compiler
1621
- const compiled = await this.blockchain.unifiedVM.compileBundle(sources);
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
+ });
1622
2174
 
1623
- // Deploy the unified bytecode
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);
1624
2212
  const address = '0x' + require('crypto').randomBytes(20).toString('hex');
1625
2213
 
1626
- // Store the unified contract
1627
2214
  if (this.blockchain.contracts) {
1628
2215
  this.blockchain.contracts.set(address, {
1629
2216
  bytecode: compiled.bytecode,
@@ -1635,15 +2222,10 @@ class EmbeddedRpcServer {
1635
2222
  });
1636
2223
  }
1637
2224
 
1638
- console.log(`[RPC] Unified bundle deployed at ${address}`);
1639
- console.log(`[RPC] Bytecode: ${compiled.bytecode?.length || 0} bytes`);
1640
- console.log(`[RPC] Cross-references: ${compiled.crossReferences?.size || 0}`);
1641
-
1642
- // Build contracts map for response
1643
2225
  const contractsMap = {};
1644
2226
  for (const source of sources) {
1645
2227
  contractsMap[source.name] = {
1646
- address: address, // All share the same bundle address
2228
+ address: address,
1647
2229
  language: source.language,
1648
2230
  name: source.name
1649
2231
  };
@@ -1798,6 +2380,131 @@ class EmbeddedRpcServer {
1798
2380
  };
1799
2381
  }
1800
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
+
1801
2508
  async stop() {
1802
2509
  if (this.server) {
1803
2510
  this.server.close();