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/README.md +48 -0
- package/lib/JAELIS-VM/lib/unified/cross-chain-deploy.js +1678 -0
- package/lib/JAELIS-VM/lib/unified/index.js +79 -1
- package/lib/index.js +733 -26
- package/lib/settlement-server.js +999 -0
- package/package.json +1 -1
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
|
-
|
|
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
|
-
//
|
|
1129
|
-
|
|
1130
|
-
|
|
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
|
|
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
|
|
1138
|
-
params
|
|
1139
|
-
id:
|
|
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
|
-
|
|
1143
|
-
|
|
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
|
-
|
|
1354
|
+
console.error('[SYNC] Error:', err.message);
|
|
1355
|
+
} finally {
|
|
1356
|
+
this.syncState.syncing = false;
|
|
1146
1357
|
}
|
|
1147
1358
|
};
|
|
1148
1359
|
|
|
1149
|
-
//
|
|
1150
|
-
|
|
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
|
-
*
|
|
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
|
-
//
|
|
1621
|
-
const
|
|
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
|
-
|
|
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,
|
|
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();
|