jaelis-node 1.2.0 → 1.3.1

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 CHANGED
@@ -1,65 +1,84 @@
1
- # @jaelis/node
1
+ # jaelis-node
2
2
 
3
- Official node software for the **JAELIS Blockchain** - run a full node, validator, or bootstrap node on the JAELIS network.
3
+ Official node software for the **JAELIS Blockchain** - the first unified multi-language smart contract platform with cross-chain settlement.
4
4
 
5
- [![npm version](https://badge.fury.io/js/@jaelis%2Fnode.svg)](https://www.npmjs.com/package/@jaelis/node)
5
+ [![npm version](https://badge.fury.io/js/jaelis-node.svg)](https://www.npmjs.com/package/jaelis-node)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
7
 
8
- ## Features
8
+ ## What is JAELIS?
9
9
 
10
- - **Full Node** - Sync and validate the entire JAELIS blockchain
11
- - **Validator Node** - Participate in PoEC consensus and earn rewards
12
- - **Bootstrap Node** - Help with peer discovery on the network
13
- - **Zero Gas Fees** - LODE-based computational metering
14
- - **Multi-VM Support** - EVM, Solana, WASM, Bitcoin, Move, TON
15
- - **AI-Native** - Built-in MCP server and x402 payment protocol
16
- - **LevelDB Storage** - Fast, persistent blockchain storage
10
+ JAELIS is a next-generation blockchain with a **Universal Virtual Machine** that natively executes smart contracts written in 6 different languages - all on the same chain, with direct cross-contract calls between them. No bridges. No wrapped tokens. No translation layers.
17
11
 
18
- ## Installation
12
+ **One chain. Six languages. Direct interoperability.**
19
13
 
20
- ### Global Installation (Recommended)
14
+ ## Universal VM - Core Innovation
21
15
 
22
- ```bash
23
- npm install -g @jaelis/node
24
- ```
16
+ The JAELIS Universal VM executes contracts from multiple ecosystems natively:
25
17
 
26
- ### Local Installation
18
+ | Language | Ecosystem | Status |
19
+ |----------|-----------|--------|
20
+ | **Solidity** | Ethereum, Polygon, Arbitrum, Base, etc. | Production |
21
+ | **Rust** | Solana, NEAR, Polkadot | Production |
22
+ | **Move** | Aptos, Sui, Movement | Production |
23
+ | **FunC** | TON (Telegram's 900M+ users) | Production |
24
+ | **Cairo** | StarkNet | Production |
25
+ | **Vyper** | Ethereum (Python-like) | Production |
27
26
 
28
- ```bash
29
- npm install @jaelis/node
30
- ```
27
+ ### Cross-Language Contract Calls
31
28
 
32
- ## Quick Start
29
+ A Solidity contract can call a Rust contract. A Move module can interact with FunC. All native, all on-chain:
33
30
 
34
- ### Start a Full Node
31
+ ```javascript
32
+ // Solidity contract calling a Rust program
33
+ await jaelis.crossContractCall({
34
+ from: solidityContract,
35
+ to: rustProgram,
36
+ method: 'process_payment',
37
+ args: [amount, recipient]
38
+ });
39
+ ```
35
40
 
36
- ```bash
37
- # Start on testnet (default)
38
- jaelis-node start
41
+ ## Cross-Chain Settlement (30+ Chains)
39
42
 
40
- # Start on testnet with custom ports
41
- jaelis-node start --rpc-port 8545 --p2p-port 30303
43
+ JAELIS nodes run light clients for 30+ external blockchains, enabling trustless state verification:
42
44
 
43
- # Start on mainnet (when available)
44
- jaelis-node start --network mainnet
45
- ```
45
+ - **EVM Chains**: Ethereum, Polygon, Arbitrum, Optimism, Base, BSC, Avalanche, Fantom
46
+ - **Non-EVM**: Solana, Cosmos, NEAR, Polkadot, Cardano, Algorand
47
+ - **Bitcoin L2**: Lightning, Liquid, Stacks
48
+ - **Emerging**: Aptos, Sui, TON, StarkNet, zkSync
46
49
 
47
- ### Check Node Status
50
+ Read external chain state directly in your JAELIS contracts - no oracles needed.
48
51
 
49
- ```bash
50
- jaelis-node status
51
- ```
52
+ ## Features
52
53
 
53
- ### View Connected Peers
54
+ - **Universal VM** - 6 languages, one chain, direct interop
55
+ - **Cross-Chain Settlement** - Read state from 30+ chains trustlessly
56
+ - **Zero Gas Fees** - LODE-based computational metering (users pay $0)
57
+ - **Multi-Chain Wallets** - Receive rewards to ANY chain address
58
+ - **No Staking Required** - JAELIS never holds your funds
59
+ - **AI-Native** - Built-in MCP server and x402 payment protocol
60
+ - **ERC-5792** - Atomic batch transactions
61
+ - **EIP-7702** - Account abstraction (EOA → Smart Account)
62
+ - **Address Safety** - Anti-phishing and poisoning protection
63
+ - **Real P2P** - libp2p networking (same as Ethereum 2.0, IPFS)
64
+ - **Lifetime Node Tracking** - Your contributions are recorded forever
65
+
66
+ ## Installation
54
67
 
55
68
  ```bash
56
- jaelis-node peers
69
+ npm install -g jaelis-node
57
70
  ```
58
71
 
59
- ### Display Node Info
72
+ ## Quick Start
60
73
 
61
74
  ```bash
62
- jaelis-node info
75
+ # Start a node (zero config - auto-connects to testnet)
76
+ jaelis-node start
77
+
78
+ # Start with your reward wallet (use ANY chain address!)
79
+ jaelis-node start --reward-recipient 0xYourEthAddress
80
+ jaelis-node start --reward-recipient 9WzDX...YourSolanaAddress
81
+ jaelis-node start --reward-recipient bc1q...YourBitcoinAddress
63
82
  ```
64
83
 
65
84
  ## Commands
@@ -67,11 +86,39 @@ jaelis-node info
67
86
  | Command | Description |
68
87
  |---------|-------------|
69
88
  | `start` | Start a full JAELIS node |
70
- | `validator` | Run as a validator node (PoEC) |
89
+ | `validator` | Run as a validator node (bonus rewards) |
71
90
  | `bootstrap` | Run as a bootstrap/seed node |
72
91
  | `status` | Check local node status |
73
92
  | `peers` | List connected peers |
74
- | `info` | Display node information |
93
+ | `info` | Display node and network information |
94
+
95
+ ## Wallet Commands
96
+
97
+ JAELIS supports **any chain address** for receiving rewards. No staking. No lockups. Your keys, your wallet.
98
+
99
+ ```bash
100
+ # Set your reward wallet (accepts ANY chain format)
101
+ jaelis-node wallet:set-recipient 0x742d35cc6634C0532925a3b844Bc454e4438f44e # EVM
102
+ jaelis-node wallet:set-recipient 9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM # Solana
103
+ jaelis-node wallet:set-recipient bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh # Bitcoin
104
+ jaelis-node wallet:set-recipient EQDrLq-X6jKZNHAScgghh0h1iog3StK71zn8dcmrOj0n # TON
105
+
106
+ # List configured wallets
107
+ jaelis-node wallet:list
108
+
109
+ # Add additional wallets
110
+ jaelis-node wallet:add "trading" 0x456...
111
+
112
+ # View node identity
113
+ jaelis-node wallet:identity
114
+ ```
115
+
116
+ **Supported Address Formats:**
117
+ - EVM (Ethereum, Polygon, Arbitrum, Base, Optimism, etc.)
118
+ - Solana (base58)
119
+ - Bitcoin (Legacy: 1..., SegWit: 3..., Native SegWit: bc1...)
120
+ - TON (base64url)
121
+ - Move (Aptos/Sui: 0x + 64 hex chars)
75
122
 
76
123
  ## Start Options
77
124
 
@@ -79,64 +126,114 @@ jaelis-node info
79
126
  jaelis-node start [options]
80
127
 
81
128
  Options:
82
- -n, --network <network> Network: testnet or mainnet (default: testnet)
83
- -r, --rpc-port <port> RPC server port (default: 8545)
84
- -p, --p2p-port <port> P2P network port (default: 30303)
85
- -d, --data-dir <path> Data directory (default: ./jaelis-data)
86
- --rpc-host <host> RPC host (default: 0.0.0.0)
87
- --no-rpc Disable RPC server
88
- --sync-mode <mode> Sync mode: full, light, archive (default: full)
129
+ -n, --network <network> Network: testnet or mainnet (default: testnet)
130
+ -r, --rpc-port <port> RPC server port (default: 8545)
131
+ -p, --p2p-port <port> P2P network port (default: 30303)
132
+ -d, --data-dir <path> Data directory (default: ./jaelis-data)
133
+ --rpc-host <host> RPC host (default: 0.0.0.0)
134
+ --no-rpc Disable RPC server
135
+ --sync-mode <mode> Sync mode: full, light, archive (default: full)
136
+ --reward-recipient <address> Wallet address for node rewards (ANY chain!)
89
137
  ```
90
138
 
139
+ ## RPC Methods
140
+
141
+ Your node exposes standard JSON-RPC plus JAELIS-specific methods:
142
+
143
+ ### Universal VM Methods
144
+ | Method | Description |
145
+ |--------|-------------|
146
+ | `jaelis_getSupportedLanguages` | Returns: `['solidity', 'rust', 'move', 'func', 'cairo', 'vyper']` |
147
+ | `jaelis_deployContract` | Deploy contract in any supported language |
148
+ | `jaelis_executeContract` | Execute contract method |
149
+ | `jaelis_crossContractCall` | Call between contracts of different languages |
150
+
151
+ ### Cross-Chain Settlement Methods
152
+ | Method | Description |
153
+ |--------|-------------|
154
+ | `jaelis_crossChain_getChains` | List all 30+ registered chains |
155
+ | `jaelis_crossChain_getChainInfo` | Get chain metadata (name, RPC, block time) |
156
+ | `jaelis_crossChain_readState` | Read settled external chain state |
157
+ | `jaelis_crossChain_settleState` | Submit proof and settle state |
158
+ | `jaelis_crossChain_getStateDiff` | Compare state across chains |
159
+ | `jaelis_crossChain_getLightClientStatus` | Check light client sync status |
160
+
161
+ ### Wallet Standard Methods (ERC-5792, EIP-7702)
162
+ | Method | Description |
163
+ |--------|-------------|
164
+ | `jaelis_wallet_getCapabilities` | Get wallet capabilities |
165
+ | `jaelis_wallet_sendCalls` | Atomic batch transactions |
166
+ | `jaelis_wallet_getCallsStatus` | Check batch status |
167
+ | `jaelis_wallet_grantPermissions` | Grant session permissions |
168
+ | `jaelis_validateAddressSafety` | Anti-phishing check |
169
+ | `jaelis_checkDustAttacks` | Detect address poisoning |
170
+
171
+ ### Standard Ethereum-Compatible Methods
172
+ | Method | Description |
173
+ |--------|-------------|
174
+ | `eth_blockNumber` | Get latest block number |
175
+ | `eth_getBalance` | Get account balance |
176
+ | `eth_sendTransaction` | Send transaction |
177
+ | `eth_call` | Call contract (read-only) |
178
+ | `eth_getTransactionReceipt` | Get transaction receipt |
179
+
91
180
  ## Programmatic Usage
92
181
 
93
182
  ```javascript
94
- const { JaelisNode, BootstrapNode } = require('@jaelis/node');
183
+ const { JaelisNode, BootstrapNode } = require('jaelis-node');
95
184
 
96
- // Create and start a full node
185
+ // Create and start a node
97
186
  const node = new JaelisNode({
98
187
  network: 'testnet',
99
188
  rpcPort: 8545,
100
189
  p2pPort: 30303,
101
- dataDir: './my-node-data'
190
+ dataDir: './my-node-data',
191
+ rewardRecipient: '0xYourAddress' // Any chain address
102
192
  });
103
193
 
104
194
  await node.start();
105
195
 
106
196
  // Get node status
107
197
  const status = node.getStatus();
108
- console.log(status);
198
+ console.log('Chain ID:', status.chainId); // 4545 (testnet) or 4547 (mainnet)
199
+ console.log('Peers:', status.peerCount);
200
+ console.log('Block:', status.blockNumber);
109
201
 
110
202
  // Listen for events
111
203
  node.on('peer:connect', (peerCount) => {
112
- console.log(`Peers connected: ${peerCount}`);
204
+ console.log(`Connected peers: ${peerCount}`);
113
205
  });
114
206
 
115
- // Stop the node
116
- await node.stop();
117
- ```
118
-
119
- ### Bootstrap Node
120
-
121
- ```javascript
122
- const { BootstrapNode } = require('@jaelis/node');
123
-
124
- const bootstrap = new BootstrapNode({
125
- port: 30305,
126
- network: 'testnet'
207
+ node.on('block:new', (block) => {
208
+ console.log(`New block: ${block.number}`);
127
209
  });
128
210
 
129
- await bootstrap.start();
130
- console.log(`Bootstrap node running, ${bootstrap.getPeerCount()} peers`);
211
+ // Stop the node
212
+ await node.stop();
131
213
  ```
132
214
 
133
215
  ## Network Configuration
134
216
 
135
217
  | Network | Chain ID | RPC URL | Status |
136
218
  |---------|----------|---------|--------|
137
- | Testnet | 4545 | https://rpc.jaelis.io | Active |
219
+ | Testnet | 4545 | https://rpc.jaelis.io | **Active** |
138
220
  | Mainnet | 4547 | https://mainnet.jaelis.io | Coming Soon |
139
221
 
222
+ ## Node Rewards
223
+
224
+ JAELIS rewards node operators based on:
225
+ - **Uptime** - Keep your node running
226
+ - **Transactions Processed** - Help validate transactions
227
+ - **Bandwidth Provided** - Serve data to the network
228
+ - **Validator Mode** - Opt-in for bonus rewards
229
+
230
+ **No staking required.** JAELIS never holds your funds. Use any wallet from any chain.
231
+
232
+ ```bash
233
+ # Check your node's contribution stats
234
+ jaelis-node status
235
+ ```
236
+
140
237
  ## System Requirements
141
238
 
142
239
  | Requirement | Minimum | Recommended |
@@ -151,11 +248,11 @@ console.log(`Bootstrap node running, ${bootstrap.getPeerCount()} peers`);
151
248
 
152
249
  ```
153
250
  jaelis-data/
154
- ├── blockchain/ # Block and state data
155
- ├── blocks/ # Block storage (LevelDB)
156
- │ └── state/ # State trie (LevelDB)
251
+ ├── blockchain/ # Block and state data (LevelDB)
252
+ ├── node-registry/ # Node tracking and statistics
253
+ ├── wallet-config/ # Wallet configuration
157
254
  ├── peers/ # Peer database
158
- ├── keys/ # Node keys (if validator)
255
+ ├── keys/ # Node identity keys
159
256
  └── logs/ # Node logs
160
257
  ```
161
258
 
@@ -163,38 +260,44 @@ jaelis-data/
163
260
 
164
261
  | Variable | Description | Default |
165
262
  |----------|-------------|---------|
166
- | `JAELIS_CORE_PATH` | Path to JAELIS core | Auto-detected |
167
- | `JAELIS_DATA_DIR` | Data directory | `./jaelis-data` |
168
263
  | `JAELIS_NETWORK` | Network (testnet/mainnet) | `testnet` |
264
+ | `JAELIS_DATA_DIR` | Data directory | `./jaelis-data` |
169
265
  | `JAELIS_RPC_PORT` | RPC port | `8545` |
170
266
  | `JAELIS_P2P_PORT` | P2P port | `30303` |
267
+ | `JAELIS_REWARD_ADDRESS` | Reward wallet address | - |
171
268
 
172
269
  ## Docker
173
270
 
174
271
  ```bash
175
- # Pull the image
176
- docker pull jaelis/node
177
-
178
272
  # Run a full node
179
273
  docker run -d \
180
274
  --name jaelis-node \
181
275
  -p 8545:8545 \
182
276
  -p 30303:30303 \
183
277
  -v jaelis-data:/data \
278
+ -e JAELIS_REWARD_ADDRESS=0xYourAddress \
184
279
  jaelis/node start --network testnet
185
-
186
- # Check logs
187
- docker logs -f jaelis-node
188
280
  ```
189
281
 
190
- ## Contributing
282
+ ## Technical Highlights
283
+
284
+ ### Patent-Pending Technology
285
+ JAELIS implements three patent-pending systems:
286
+ 1. **Universal VM Architecture** - Multi-language contract execution
287
+ 2. **Cross-Chain Light Client Protocol** - Trustless external state verification
288
+ 3. **LODE Computation Model** - Zero-fee transaction processing
191
289
 
192
- Contributions are welcome! Please see our contributing guidelines.
290
+ ### Security
291
+ - Real libp2p P2P networking (same protocol as Ethereum 2.0, Filecoin, IPFS)
292
+ - Automatic peer discovery and DHT routing
293
+ - Built-in MEV protection
294
+ - Address poisoning detection
295
+ - No trusted setup for privacy features
193
296
 
194
297
  ## Links
195
298
 
299
+ - SDK: [npmjs.com/package/jaelis.js](https://www.npmjs.com/package/jaelis.js)
196
300
  - Website: [jaelis.io](https://jaelis.io)
197
- - SDK: [npm/jaelis.js](https://www.npmjs.com/package/jaelis.js)
198
301
  - Documentation: [docs.jaelis.io](https://docs.jaelis.io)
199
302
  - GitHub: [github.com/jaelis-foundation](https://github.com/jaelis-foundation)
200
303
 
@@ -76,6 +76,7 @@ program
76
76
  .option('--rpc-host <host>', 'RPC server host', '0.0.0.0')
77
77
  .option('--no-rpc', 'Disable RPC server')
78
78
  .option('--sync-mode <mode>', 'Sync mode: full, light, archive', 'full')
79
+ .option('--reward-recipient <address>', 'Wallet address to receive node rewards (ANY chain format!)')
79
80
  .action(async (options) => {
80
81
  console.log(chalk.cyan(BANNER));
81
82
  console.log(chalk.green('Starting JAELIS Node...'));
@@ -94,6 +95,9 @@ program
94
95
  console.log(chalk.gray(` P2P Port: ${options.p2pPort}`));
95
96
  console.log(chalk.gray(` Data Dir: ${options.dataDir}`));
96
97
  console.log(chalk.gray(` Sync Mode: ${options.syncMode}`));
98
+ if (options.rewardRecipient) {
99
+ console.log(chalk.gray(` Rewards To: ${options.rewardRecipient}`));
100
+ }
97
101
  console.log();
98
102
 
99
103
  const spinner = ora('Initializing node...').start();
@@ -111,7 +115,8 @@ program
111
115
  dataDir: options.dataDir,
112
116
  syncMode: options.syncMode,
113
117
  enableRpc: options.rpc,
114
- bootstrapNodes: network.bootstrapNodes
118
+ bootstrapNodes: network.bootstrapNodes,
119
+ rewardRecipient: options.rewardRecipient // Like Geth's --suggested-fee-recipient
115
120
  });
116
121
 
117
122
  spinner.text = 'Connecting to network...';
@@ -311,5 +316,201 @@ program
311
316
  console.log(chalk.cyan('Website: https://jaelis.io'));
312
317
  });
313
318
 
319
+ // ═══════════════════════════════════════════════════════════════════════════
320
+ // WALLET COMMANDS - Manage node wallet configuration
321
+ // Like Geth's account management, but supports ANY chain address!
322
+ // NO STAKING REQUIRED - JAELIS doesn't hold your funds!
323
+ // ═══════════════════════════════════════════════════════════════════════════
324
+
325
+ // WALLET SET-RECIPIENT - Set reward recipient address (like Geth --suggested-fee-recipient)
326
+ program
327
+ .command('wallet:set-recipient <address>')
328
+ .description('Set the wallet address to receive node rewards (accepts ANY chain format!)')
329
+ .option('-d, --data-dir <path>', 'Data directory', './jaelis-data')
330
+ .action((address, options) => {
331
+ console.log(chalk.cyan('Setting reward recipient...'));
332
+ console.log();
333
+
334
+ try {
335
+ const { NodeWalletConfig, validateAddress } = require('../lib/index.js');
336
+
337
+ // Validate address format
338
+ const validation = validateAddress(address);
339
+ if (!validation.valid) {
340
+ console.log(chalk.red('✗ Invalid address format'));
341
+ console.log(chalk.gray(' Supported formats:'));
342
+ console.log(chalk.gray(' • EVM (Ethereum, Polygon, etc.): 0x...'));
343
+ console.log(chalk.gray(' • Solana: base58 address'));
344
+ console.log(chalk.gray(' • Bitcoin: 1..., 3..., or bc1...'));
345
+ console.log(chalk.gray(' • TON: base64url address'));
346
+ console.log(chalk.gray(' • Move (Aptos/Sui): 0x... (64 chars)'));
347
+ process.exit(1);
348
+ }
349
+
350
+ const walletConfig = new NodeWalletConfig(options.dataDir);
351
+ walletConfig.load();
352
+ const result = walletConfig.setRewardRecipient(address);
353
+
354
+ console.log(chalk.green('✓ Reward recipient set successfully!'));
355
+ console.log();
356
+ console.log(chalk.white(' Address: ') + chalk.cyan(result.originalFormat));
357
+ console.log(chalk.white(' Type: ') + chalk.gray(result.typeName));
358
+ console.log();
359
+ console.log(chalk.gray(' Node rewards will be sent to this address.'));
360
+ console.log(chalk.gray(' NO STAKING REQUIRED - you keep your own funds!'));
361
+
362
+ } catch (error) {
363
+ console.log(chalk.red('✗ Failed to set recipient'));
364
+ console.log(chalk.gray(error.message));
365
+ process.exit(1);
366
+ }
367
+ });
368
+
369
+ // WALLET ADD - Add an additional wallet
370
+ program
371
+ .command('wallet:add <name> <address>')
372
+ .description('Add a wallet to this node (accepts ANY chain format!)')
373
+ .option('-d, --data-dir <path>', 'Data directory', './jaelis-data')
374
+ .option('-p, --purpose <purpose>', 'Wallet purpose (general, validator, development)', 'general')
375
+ .action((name, address, options) => {
376
+ console.log(chalk.cyan('Adding wallet...'));
377
+ console.log();
378
+
379
+ try {
380
+ const { NodeWalletConfig, validateAddress } = require('../lib/index.js');
381
+
382
+ const validation = validateAddress(address);
383
+ if (!validation.valid) {
384
+ console.log(chalk.red('✗ Invalid address format'));
385
+ process.exit(1);
386
+ }
387
+
388
+ const walletConfig = new NodeWalletConfig(options.dataDir);
389
+ walletConfig.load();
390
+ const result = walletConfig.addWallet(name, address, { purpose: options.purpose });
391
+
392
+ console.log(chalk.green('✓ Wallet added successfully!'));
393
+ console.log();
394
+ console.log(chalk.white(' Name: ') + chalk.cyan(result.name));
395
+ console.log(chalk.white(' Address: ') + chalk.gray(result.originalFormat));
396
+ console.log(chalk.white(' Type: ') + chalk.gray(result.typeName));
397
+ console.log(chalk.white(' Purpose: ') + chalk.gray(result.purpose));
398
+
399
+ } catch (error) {
400
+ console.log(chalk.red('✗ Failed to add wallet'));
401
+ console.log(chalk.gray(error.message));
402
+ process.exit(1);
403
+ }
404
+ });
405
+
406
+ // WALLET REMOVE - Remove a wallet
407
+ program
408
+ .command('wallet:remove <name-or-address>')
409
+ .description('Remove a wallet from this node')
410
+ .option('-d, --data-dir <path>', 'Data directory', './jaelis-data')
411
+ .action((nameOrAddress, options) => {
412
+ console.log(chalk.cyan('Removing wallet...'));
413
+ console.log();
414
+
415
+ try {
416
+ const { NodeWalletConfig } = require('../lib/index.js');
417
+
418
+ const walletConfig = new NodeWalletConfig(options.dataDir);
419
+ walletConfig.load();
420
+ const result = walletConfig.removeWallet(nameOrAddress);
421
+
422
+ console.log(chalk.green('✓ Wallet removed!'));
423
+ console.log(chalk.gray(` Removed: ${result.name} (${result.originalFormat})`));
424
+
425
+ } catch (error) {
426
+ console.log(chalk.red('✗ Failed to remove wallet'));
427
+ console.log(chalk.gray(error.message));
428
+ process.exit(1);
429
+ }
430
+ });
431
+
432
+ // WALLET LIST - List all wallets
433
+ program
434
+ .command('wallet:list')
435
+ .description('List all configured wallets')
436
+ .option('-d, --data-dir <path>', 'Data directory', './jaelis-data')
437
+ .action((options) => {
438
+ console.log(chalk.cyan('Node Wallet Configuration'));
439
+ console.log();
440
+
441
+ try {
442
+ const { NodeWalletConfig } = require('../lib/index.js');
443
+
444
+ const walletConfig = new NodeWalletConfig(options.dataDir);
445
+ const config = walletConfig.listWallets();
446
+
447
+ // Show reward recipient
448
+ if (config.rewardRecipient) {
449
+ console.log(chalk.white('Reward Recipient:'));
450
+ console.log(chalk.green(` ★ ${config.rewardRecipient.originalFormat}`));
451
+ console.log(chalk.gray(` Type: ${config.rewardRecipient.typeName}`));
452
+ console.log();
453
+ } else {
454
+ console.log(chalk.yellow('⚠ No reward recipient set!'));
455
+ console.log(chalk.gray(' Run: jaelis-node wallet:set-recipient <your-address>'));
456
+ console.log();
457
+ }
458
+
459
+ // Show additional wallets
460
+ if (config.wallets && config.wallets.length > 0) {
461
+ console.log(chalk.white('Additional Wallets:'));
462
+ config.wallets.forEach((wallet, i) => {
463
+ console.log(chalk.cyan(` ${i + 1}. ${wallet.name}`));
464
+ console.log(chalk.gray(` Address: ${wallet.originalFormat}`));
465
+ console.log(chalk.gray(` Type: ${wallet.typeName}`));
466
+ console.log(chalk.gray(` Purpose: ${wallet.purpose}`));
467
+ });
468
+ } else {
469
+ console.log(chalk.gray('No additional wallets configured.'));
470
+ }
471
+
472
+ console.log();
473
+ console.log(chalk.gray('Supported address formats:'));
474
+ console.log(chalk.gray(' • EVM (Ethereum, Polygon, Arbitrum, Base, etc.)'));
475
+ console.log(chalk.gray(' • Solana (base58)'));
476
+ console.log(chalk.gray(' • Bitcoin (Legacy & SegWit)'));
477
+ console.log(chalk.gray(' • TON'));
478
+ console.log(chalk.gray(' • Move (Aptos/Sui)'));
479
+
480
+ } catch (error) {
481
+ console.log(chalk.red('Failed to list wallets'));
482
+ console.log(chalk.gray(error.message));
483
+ }
484
+ });
485
+
486
+ // WALLET IDENTITY - Show or generate node identity
487
+ program
488
+ .command('wallet:identity')
489
+ .description('Show or generate the node identity (like Solana identity keypair)')
490
+ .option('-d, --data-dir <path>', 'Data directory', './jaelis-data')
491
+ .action((options) => {
492
+ console.log(chalk.cyan('Node Identity'));
493
+ console.log();
494
+
495
+ try {
496
+ const { NodeWalletConfig } = require('../lib/index.js');
497
+
498
+ const walletConfig = new NodeWalletConfig(options.dataDir);
499
+ walletConfig.load();
500
+ const identity = walletConfig.getNodeIdentity();
501
+
502
+ console.log(chalk.white('Node Identity:'));
503
+ console.log(chalk.green(` Public Key: ${identity.publicKey}`));
504
+ console.log(chalk.gray(` Created: ${new Date(identity.createdAt).toISOString()}`));
505
+ console.log();
506
+ console.log(chalk.gray('This identity is used to identify your node on the network.'));
507
+ console.log(chalk.gray('It is NOT a wallet - use wallet:set-recipient for rewards.'));
508
+
509
+ } catch (error) {
510
+ console.log(chalk.red('Failed to get node identity'));
511
+ console.log(chalk.gray(error.message));
512
+ }
513
+ });
514
+
314
515
  // Parse and execute
315
516
  program.parse();
package/lib/index.js CHANGED
@@ -11,8 +11,310 @@
11
11
 
12
12
  const path = require('path');
13
13
  const fs = require('fs');
14
+ const crypto = require('crypto');
14
15
  const EventEmitter = require('events');
15
16
 
17
+ // ═══════════════════════════════════════════════════════════════════════════
18
+ // MULTI-CHAIN ADDRESS UTILITIES
19
+ // Supports: EVM (0x...), Solana (base58), Bitcoin (P2PKH/bech32), etc.
20
+ // JAELIS accepts ANY chain's address format - TRUE multi-chain!
21
+ // ═══════════════════════════════════════════════════════════════════════════
22
+
23
+ const ADDRESS_FORMATS = {
24
+ // EVM chains (Ethereum, Polygon, Arbitrum, etc.)
25
+ evm: {
26
+ pattern: /^0x[a-fA-F0-9]{40}$/,
27
+ name: 'EVM (Ethereum, Polygon, etc.)',
28
+ example: '0x742d35Cc6634C0532925a3b844Bc9e7595f...'
29
+ },
30
+ // Solana (base58)
31
+ solana: {
32
+ pattern: /^[1-9A-HJ-NP-Za-km-z]{32,44}$/,
33
+ name: 'Solana',
34
+ example: '7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZ...'
35
+ },
36
+ // Bitcoin P2PKH (starts with 1 or 3)
37
+ bitcoin_legacy: {
38
+ pattern: /^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$/,
39
+ name: 'Bitcoin (Legacy)',
40
+ example: '1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2'
41
+ },
42
+ // Bitcoin bech32 (starts with bc1)
43
+ bitcoin_bech32: {
44
+ pattern: /^bc1[a-z0-9]{39,59}$/,
45
+ name: 'Bitcoin (SegWit)',
46
+ example: 'bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq'
47
+ },
48
+ // TON (base64url)
49
+ ton: {
50
+ pattern: /^[A-Za-z0-9_-]{48}$/,
51
+ name: 'TON',
52
+ example: 'EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N'
53
+ },
54
+ // Aptos/Sui (hex with 0x, 64 chars)
55
+ move: {
56
+ pattern: /^0x[a-fA-F0-9]{64}$/,
57
+ name: 'Move (Aptos/Sui)',
58
+ example: '0x1234567890abcdef...'
59
+ },
60
+ // JAELIS native (same as EVM for now, but could be extended)
61
+ jaelis: {
62
+ pattern: /^0x[a-fA-F0-9]{40}$/,
63
+ name: 'JAELIS Native',
64
+ example: '0x742d35Cc6634C0532925a3b844Bc9e7595f...'
65
+ }
66
+ };
67
+
68
+ /**
69
+ * Detect the chain type from an address
70
+ */
71
+ function detectAddressType(address) {
72
+ if (!address || typeof address !== 'string') {
73
+ return null;
74
+ }
75
+
76
+ // Check each format
77
+ for (const [type, config] of Object.entries(ADDRESS_FORMATS)) {
78
+ if (config.pattern.test(address)) {
79
+ return { type, ...config };
80
+ }
81
+ }
82
+
83
+ return null;
84
+ }
85
+
86
+ /**
87
+ * Validate any chain address
88
+ */
89
+ function validateAddress(address) {
90
+ const detected = detectAddressType(address);
91
+ if (!detected) {
92
+ return {
93
+ valid: false,
94
+ error: 'Unknown address format',
95
+ supportedFormats: Object.keys(ADDRESS_FORMATS)
96
+ };
97
+ }
98
+ return {
99
+ valid: true,
100
+ type: detected.type,
101
+ name: detected.name
102
+ };
103
+ }
104
+
105
+ /**
106
+ * Convert any address to canonical form for internal storage
107
+ * EVM addresses are lowercased, others kept as-is
108
+ */
109
+ function toCanonicalAddress(address) {
110
+ const detected = detectAddressType(address);
111
+ if (!detected) {
112
+ throw new Error('Invalid address format');
113
+ }
114
+
115
+ // EVM addresses are case-insensitive, normalize to lowercase
116
+ if (detected.type === 'evm' || detected.type === 'jaelis') {
117
+ return address.toLowerCase();
118
+ }
119
+
120
+ // Other formats are case-sensitive
121
+ return address;
122
+ }
123
+
124
+ // ═══════════════════════════════════════════════════════════════════════════
125
+ // NODE WALLET CONFIGURATION (Like Geth --suggested-fee-recipient)
126
+ // Stores the node operator's wallet for receiving rewards
127
+ // NO STAKING REQUIRED - JAELIS doesn't hold your funds!
128
+ // ═══════════════════════════════════════════════════════════════════════════
129
+
130
+ class NodeWalletConfig {
131
+ constructor(dataDir) {
132
+ this.dataDir = dataDir;
133
+ this.configPath = path.join(dataDir, 'node-wallet.json');
134
+ this.config = null;
135
+ }
136
+
137
+ /**
138
+ * Load wallet configuration from disk
139
+ */
140
+ load() {
141
+ try {
142
+ if (fs.existsSync(this.configPath)) {
143
+ const data = fs.readFileSync(this.configPath, 'utf8');
144
+ this.config = JSON.parse(data);
145
+ return this.config;
146
+ }
147
+ } catch (e) {
148
+ console.warn('[WALLET] Failed to load wallet config:', e.message);
149
+ }
150
+ return null;
151
+ }
152
+
153
+ /**
154
+ * Save wallet configuration to disk
155
+ */
156
+ save() {
157
+ try {
158
+ // Ensure directory exists
159
+ if (!fs.existsSync(this.dataDir)) {
160
+ fs.mkdirSync(this.dataDir, { recursive: true });
161
+ }
162
+ fs.writeFileSync(this.configPath, JSON.stringify(this.config, null, 2));
163
+ return true;
164
+ } catch (e) {
165
+ console.error('[WALLET] Failed to save wallet config:', e.message);
166
+ return false;
167
+ }
168
+ }
169
+
170
+ /**
171
+ * Set the reward recipient wallet (ANY chain address!)
172
+ * Like Geth's --suggested-fee-recipient
173
+ */
174
+ setRewardRecipient(address) {
175
+ const validation = validateAddress(address);
176
+ if (!validation.valid) {
177
+ throw new Error(`Invalid address: ${validation.error}`);
178
+ }
179
+
180
+ if (!this.config) {
181
+ this.config = { createdAt: Date.now() };
182
+ }
183
+
184
+ this.config.rewardRecipient = {
185
+ address: toCanonicalAddress(address),
186
+ originalFormat: address,
187
+ type: validation.type,
188
+ typeName: validation.name,
189
+ setAt: Date.now()
190
+ };
191
+
192
+ this.save();
193
+ console.log(`[WALLET] Reward recipient set: ${address} (${validation.name})`);
194
+ return this.config.rewardRecipient;
195
+ }
196
+
197
+ /**
198
+ * Get the reward recipient address
199
+ */
200
+ getRewardRecipient() {
201
+ if (!this.config) this.load();
202
+ return this.config?.rewardRecipient || null;
203
+ }
204
+
205
+ /**
206
+ * Add an additional wallet (for multi-wallet nodes)
207
+ */
208
+ addWallet(name, address, options = {}) {
209
+ const validation = validateAddress(address);
210
+ if (!validation.valid) {
211
+ throw new Error(`Invalid address: ${validation.error}`);
212
+ }
213
+
214
+ if (!this.config) {
215
+ this.config = { createdAt: Date.now() };
216
+ }
217
+
218
+ if (!this.config.wallets) {
219
+ this.config.wallets = [];
220
+ }
221
+
222
+ // Check for duplicates
223
+ const existing = this.config.wallets.find(w =>
224
+ toCanonicalAddress(w.address) === toCanonicalAddress(address)
225
+ );
226
+ if (existing) {
227
+ throw new Error(`Wallet already exists: ${existing.name}`);
228
+ }
229
+
230
+ const wallet = {
231
+ name,
232
+ address: toCanonicalAddress(address),
233
+ originalFormat: address,
234
+ type: validation.type,
235
+ typeName: validation.name,
236
+ purpose: options.purpose || 'general',
237
+ addedAt: Date.now()
238
+ };
239
+
240
+ this.config.wallets.push(wallet);
241
+ this.save();
242
+ console.log(`[WALLET] Added wallet: ${name} (${validation.name})`);
243
+ return wallet;
244
+ }
245
+
246
+ /**
247
+ * Remove a wallet by name or address
248
+ */
249
+ removeWallet(nameOrAddress) {
250
+ if (!this.config?.wallets) {
251
+ throw new Error('No wallets configured');
252
+ }
253
+
254
+ const canonical = nameOrAddress.startsWith('0x') || nameOrAddress.startsWith('bc1')
255
+ ? toCanonicalAddress(nameOrAddress)
256
+ : null;
257
+
258
+ const index = this.config.wallets.findIndex(w =>
259
+ w.name === nameOrAddress ||
260
+ (canonical && toCanonicalAddress(w.address) === canonical)
261
+ );
262
+
263
+ if (index === -1) {
264
+ throw new Error(`Wallet not found: ${nameOrAddress}`);
265
+ }
266
+
267
+ const removed = this.config.wallets.splice(index, 1)[0];
268
+ this.save();
269
+ console.log(`[WALLET] Removed wallet: ${removed.name}`);
270
+ return removed;
271
+ }
272
+
273
+ /**
274
+ * List all configured wallets
275
+ */
276
+ listWallets() {
277
+ if (!this.config) this.load();
278
+ return {
279
+ rewardRecipient: this.config?.rewardRecipient || null,
280
+ wallets: this.config?.wallets || []
281
+ };
282
+ }
283
+
284
+ /**
285
+ * Get node identity (generates one if not exists)
286
+ * Like Solana's identity keypair
287
+ */
288
+ getNodeIdentity() {
289
+ if (!this.config) this.load();
290
+ if (!this.config) {
291
+ this.config = { createdAt: Date.now() };
292
+ }
293
+
294
+ if (!this.config.nodeIdentity) {
295
+ // Generate a node identity (not a wallet, just an ID)
296
+ const nodeId = crypto.randomBytes(32).toString('hex');
297
+ this.config.nodeIdentity = {
298
+ id: nodeId,
299
+ publicKey: '0x' + crypto.createHash('sha256').update(nodeId).digest('hex').slice(0, 40),
300
+ createdAt: Date.now()
301
+ };
302
+ this.save();
303
+ console.log(`[WALLET] Generated node identity: ${this.config.nodeIdentity.publicKey}`);
304
+ }
305
+
306
+ return this.config.nodeIdentity;
307
+ }
308
+
309
+ /**
310
+ * Get full configuration
311
+ */
312
+ getConfig() {
313
+ if (!this.config) this.load();
314
+ return this.config;
315
+ }
316
+ }
317
+
16
318
  // JAELIS Network Endpoints - users connect here automatically
17
319
  const JAELIS_NETWORKS = {
18
320
  testnet: {
@@ -73,6 +375,65 @@ class JaelisNode extends EventEmitter {
73
375
  this.isRunning = false;
74
376
  this.startTime = null;
75
377
  this.peerCount = 0;
378
+
379
+ // Initialize wallet configuration (like Geth's keystore)
380
+ this.walletConfig = new NodeWalletConfig(this.options.dataDir);
381
+ this.walletConfig.load();
382
+
383
+ // Set reward recipient if provided via options (like --suggested-fee-recipient)
384
+ if (options.rewardRecipient) {
385
+ this.walletConfig.setRewardRecipient(options.rewardRecipient);
386
+ }
387
+ }
388
+
389
+ /**
390
+ * Get the wallet configuration manager
391
+ */
392
+ getWalletConfig() {
393
+ return this.walletConfig;
394
+ }
395
+
396
+ /**
397
+ * Set the reward recipient address (ANY chain format!)
398
+ * Like Geth's --suggested-fee-recipient
399
+ */
400
+ setRewardRecipient(address) {
401
+ return this.walletConfig.setRewardRecipient(address);
402
+ }
403
+
404
+ /**
405
+ * Get the reward recipient address
406
+ */
407
+ getRewardRecipient() {
408
+ return this.walletConfig.getRewardRecipient();
409
+ }
410
+
411
+ /**
412
+ * Add a wallet to this node
413
+ */
414
+ addWallet(name, address, options = {}) {
415
+ return this.walletConfig.addWallet(name, address, options);
416
+ }
417
+
418
+ /**
419
+ * Remove a wallet from this node
420
+ */
421
+ removeWallet(nameOrAddress) {
422
+ return this.walletConfig.removeWallet(nameOrAddress);
423
+ }
424
+
425
+ /**
426
+ * List all configured wallets
427
+ */
428
+ listWallets() {
429
+ return this.walletConfig.listWallets();
430
+ }
431
+
432
+ /**
433
+ * Get node identity
434
+ */
435
+ getNodeIdentity() {
436
+ return this.walletConfig.getNodeIdentity();
76
437
  }
77
438
 
78
439
  /**
@@ -110,6 +471,12 @@ class JaelisNode extends EventEmitter {
110
471
  this.isRunning = true;
111
472
  this.startTime = Date.now();
112
473
 
474
+ // Register with the JAELIS network for tracking
475
+ await this._registerWithNetwork();
476
+
477
+ // Start heartbeat to keep registration alive
478
+ this._startHeartbeat();
479
+
113
480
  this.emit('started', {
114
481
  network: this.options.network,
115
482
  chainId: this.options.chainId,
@@ -135,6 +502,9 @@ class JaelisNode extends EventEmitter {
135
502
 
136
503
  console.log('[JAELIS] Stopping node...');
137
504
 
505
+ // Stop heartbeat first
506
+ this._stopHeartbeat();
507
+
138
508
  // Stop RPC server
139
509
  if (this.rpcServer) {
140
510
  await this._stopRpcServer();
@@ -275,6 +645,141 @@ class JaelisNode extends EventEmitter {
275
645
  }
276
646
  }
277
647
 
648
+ /**
649
+ * Register this node with the JAELIS network for tracking
650
+ * Mario can see all nodes that connect! Lifetime tracking!
651
+ */
652
+ async _registerWithNetwork() {
653
+ try {
654
+ const https = require('https');
655
+ const http = require('http');
656
+
657
+ const identity = this.walletConfig.getNodeIdentity();
658
+ const rewardRecipient = this.walletConfig.getRewardRecipient();
659
+
660
+ const nodeInfo = {
661
+ nodeId: identity.id,
662
+ publicKey: identity.publicKey,
663
+ network: this.options.network,
664
+ chainId: this.options.chainId,
665
+ version: '1.3.0',
666
+ rewardAddress: rewardRecipient?.address || null,
667
+ rewardChainType: rewardRecipient?.type || null,
668
+ capabilities: ['full-node', 'rpc'],
669
+ p2pPort: this.options.p2pPort,
670
+ rpcPort: this.options.rpcPort
671
+ };
672
+
673
+ // Store for heartbeat
674
+ this._nodeInfo = nodeInfo;
675
+
676
+ const data = JSON.stringify({
677
+ jsonrpc: '2.0',
678
+ method: 'jaelis_node_register',
679
+ params: [nodeInfo],
680
+ id: 1
681
+ });
682
+
683
+ const url = new URL(this.options.remoteRpc);
684
+ const protocol = url.protocol === 'https:' ? https : http;
685
+
686
+ const options = {
687
+ hostname: url.hostname,
688
+ port: url.port || (url.protocol === 'https:' ? 443 : 80),
689
+ path: url.pathname || '/',
690
+ method: 'POST',
691
+ headers: {
692
+ 'Content-Type': 'application/json',
693
+ 'Content-Length': Buffer.byteLength(data)
694
+ }
695
+ };
696
+
697
+ await new Promise((resolve) => {
698
+ const req = protocol.request(options, (res) => {
699
+ let body = '';
700
+ res.on('data', chunk => body += chunk);
701
+ res.on('end', () => {
702
+ try {
703
+ const result = JSON.parse(body);
704
+ if (result.result?.registered) {
705
+ console.log(`[JAELIS] Node registered with network (ID: ${identity.publicKey.slice(0, 12)}...)`);
706
+ }
707
+ } catch (e) {
708
+ // Silent - registration is best-effort
709
+ }
710
+ resolve();
711
+ });
712
+ });
713
+
714
+ req.on('error', () => resolve()); // Silent fail - will retry on heartbeat
715
+ req.write(data);
716
+ req.end();
717
+ });
718
+
719
+ } catch (error) {
720
+ // Registration is best-effort, don't fail node startup
721
+ console.log('[JAELIS] Network registration pending (will retry)');
722
+ }
723
+ }
724
+
725
+ /**
726
+ * Start heartbeat to keep node registration alive
727
+ * Sends heartbeat every 60 seconds
728
+ */
729
+ _startHeartbeat() {
730
+ const HEARTBEAT_INTERVAL = 60000; // 60 seconds
731
+
732
+ this._heartbeatInterval = setInterval(async () => {
733
+ if (!this.isRunning || !this._nodeInfo) return;
734
+
735
+ try {
736
+ const https = require('https');
737
+ const http = require('http');
738
+
739
+ const data = JSON.stringify({
740
+ jsonrpc: '2.0',
741
+ method: 'jaelis_node_heartbeat',
742
+ params: [this._nodeInfo.nodeId],
743
+ id: 1
744
+ });
745
+
746
+ const url = new URL(this.options.remoteRpc);
747
+ const protocol = url.protocol === 'https:' ? https : http;
748
+
749
+ const options = {
750
+ hostname: url.hostname,
751
+ port: url.port || (url.protocol === 'https:' ? 443 : 80),
752
+ path: url.pathname || '/',
753
+ method: 'POST',
754
+ headers: {
755
+ 'Content-Type': 'application/json',
756
+ 'Content-Length': Buffer.byteLength(data)
757
+ }
758
+ };
759
+
760
+ const req = protocol.request(options, () => {});
761
+ req.on('error', () => {}); // Silent
762
+ req.write(data);
763
+ req.end();
764
+
765
+ } catch (e) {
766
+ // Silent - heartbeat is best-effort
767
+ }
768
+ }, HEARTBEAT_INTERVAL);
769
+
770
+ console.log('[JAELIS] Heartbeat started (60s interval)');
771
+ }
772
+
773
+ /**
774
+ * Stop heartbeat
775
+ */
776
+ _stopHeartbeat() {
777
+ if (this._heartbeatInterval) {
778
+ clearInterval(this._heartbeatInterval);
779
+ this._heartbeatInterval = null;
780
+ }
781
+ }
782
+
278
783
  /**
279
784
  * Get node status
280
785
  */
@@ -1020,6 +1525,15 @@ module.exports = {
1020
1525
  EmbeddedRpcServer,
1021
1526
  EmbeddedStorage,
1022
1527
 
1528
+ // Wallet configuration (like Geth's keystore)
1529
+ NodeWalletConfig,
1530
+
1531
+ // Multi-chain address utilities
1532
+ ADDRESS_FORMATS,
1533
+ detectAddressType,
1534
+ validateAddress,
1535
+ toCanonicalAddress,
1536
+
1023
1537
  // Convenience factory
1024
1538
  createNode: (options) => new JaelisNode(options),
1025
1539
  createBootstrap: (options) => new BootstrapNode(options)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "jaelis-node",
3
- "version": "1.2.0",
4
- "description": "Official JAELIS Blockchain Node - Run a full node, validator, or bootstrap node with Cross-Chain Settlement (30+ chains!)",
3
+ "version": "1.3.1",
4
+ "description": "Official JAELIS Blockchain Node - Run a full node, validator, or bootstrap node with Cross-Chain Settlement (30+ chains!), ERC-5792 wallet capabilities, EIP-7702 account abstraction",
5
5
  "main": "lib/index.js",
6
6
  "bin": {
7
7
  "jaelis-node": "./bin/jaelis-node.js"