Skip to content

Commit

Permalink
[refactor] restructuring project to support mainnet beta and sepolia
Browse files Browse the repository at this point in the history
  • Loading branch information
tavakyan committed Jul 29, 2024
1 parent b4b4797 commit 97cb2e5
Show file tree
Hide file tree
Showing 11 changed files with 1,349 additions and 68 deletions.
6 changes: 6 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"semi": true,
"singleQuote": true,
"trailingComma": "all"
}

64 changes: 49 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Deposit on Testnet
# Eclipse Deposit CLI

This script allows end users to deposit Ether from Sepolia to the Eclipse test network.
This CLI tool allows end users to deposit Ether from Ethereum Mainnet or the Sepolia test network into the Eclipse rollup, which utilizes the Solana Virtual Machine (SVM).

## Prerequisites

Expand All @@ -14,8 +14,8 @@ An Ethereum wallet such as Phantom or Metamask is needed.

For Metamask:
1. Choose the account you wish to use and copy its address.
2. Visit the [Sepolia faucet](https://sepoliafaucet.com/) to airdrop tokens to yourself.
3. To obtain your private key for later use, navigate to 'account details' in Metamask and select 'reveal private key'.
2. Visit the [Sepolia faucet](https://sepoliafaucet.com/) to airdrop tokens to yourself, if using Sepolia.
3. Navigate to 'account details' in MetaMask and select 'reveal private key'. Store this key in a secure file.

### Solana CLI

Expand All @@ -26,19 +26,53 @@ To generate a wallet for deposits:
2. To generate a wallet:
- Execute `solana-keygen new --no-outfile` or `solana-keygen new --outfile my-wallet.json`.
3. Copy the public key from the output, which should resemble `6g8wB6cJbodeYaEb5aD9QYqhdxiS8igfcHpz36oHY7p8`.
## Installation

1. Clone this repository:
```bash
git clone https://github.com/Eclipse-Laboratories-Inc/eclipse-deposit.git
cd eclipse-deposit
```

2. Install the necessary dependencies:
```bash
yarn install
```

## Create a Deposit

1. Obtain and set up the deposit.js script:
- Clone this repository
- Install script dependencies:
- `yarn install`
2. Execute the script:
- `node deposit.js [Solana Address] 0x11b8db6bb77ad8cb9af09d0867bb6b92477dd68e [Amount in Gwei] [Ethereum Private Key] https://rpc.sepolia.org
1. Run the CLI tool with the necessary options:
```bash
eclipse-deposit deposit -k <path_to_private_key> -d <solana_destination_address> -a <amount_in_ether> --mainnet|--sepolia [-r <rpc_url>]
```

For example:

**Mainnet Deposit:**
```bash
eclipse-deposit deposit -k /path/to/private-key.txt -d 6g8wB6cJbodeYaEb5aD9QYqhdxiS8igfcHpz36oHY7p8 -a 0.01 --mainnet
```

**Sepolia Testnet Deposit:**
```bash
eclipse-deposit deposit -k /path/to/private-key.txt -d 6g8wB6cJbodeYaEb5aD9QYqhdxiS8igfcHpz36oHY7p8 -a 0.01 --sepolia
```

**With Custom RPC URL:**
```bash
eclipse-deposit deposit -k /path/to/private-key.txt -d 6g8wB6cJbodeYaEb5aD9QYqhdxiS8igfcHpz36oHY7p8 -a 0.01 --mainnet -r https://custom-rpc-url
```

- The `-k, --key-file` option specifies the path to the Ethereum private key file.
- The `-d, --destination` option specifies the Solana destination address on the rollup (base58 encoded).
- The `-a, --amount` option specifies the amount of Ether to deposit.
- Use `--mainnet` or `--sepolia` to select the network. The tool will use different contract addresses depending on the network.
- The `-r, --rpc-url` option is optional and allows overriding the default JSON RPC URL.

## Security Note

Keep your Ethereum private key secure. Do not share it publicly or expose it in untrusted environments.

Details:
- The `[Solana Address]` is the one you generated using the Solana CLI or Phantom.
- The `[Ethereum Private Key]` is sourced from Metamask.
- `[Amount in Gwei]` is the desired deposit amount, with a minimum of '1500000' gwei (0.0015 ETH).
## Support

Please reach out to [email protected] with additional questions
For issues or questions, please contact [email protected].
29 changes: 29 additions & 0 deletions bin/cli.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/usr/bin/env node

const { Command } = require('commander');
const { runDeposit } = require('../src/lib');
const program = new Command();

program
.name('eclipse-deposit')
.description('CLI to deposit Ether into the Eclipse rollup')
.version('2.0.0');

program
.command('deposit')
.description('Deposit Ether into the Eclipse rollup')
.requiredOption('-k, --key-file <path>', 'Path to the Ethereum private key file')
.requiredOption('-d, --destination <address>', 'Destination address on the rollup (base58 encoded)')
.requiredOption('-a, --amount <ether>', 'Amount in Ether to deposit')
.option('--mainnet', 'Use Ethereum Mainnet')
.option('--sepolia', 'Use Sepolia test network')
.option('-r, --rpc-url <url>', 'Optional: JSON RPC URL to override the default')
.action((options) => {
if (!options.mainnet && !options.sepolia) {
console.error('Error: You must specify either --mainnet or --sepolia');
process.exit(1);
}
runDeposit(options);
});

program.parse(process.argv);
File renamed without changes.
33 changes: 33 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// eslint.config.js

import { FlatCompat } from '@eslint/eslintrc';
import js from '@eslint/js';
import prettier from 'eslint-config-prettier';
import prettierPlugin from 'eslint-plugin-prettier';
import globals from 'globals';

const compat = new FlatCompat({
baseDirectory: import.meta.url,
});

export default [
js.configs.recommended,
prettier,
{
files: ["**/*.js"],
languageOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
globals: {
...globals.node,
...globals.es2021,
},
},
plugins: {
prettier: prettierPlugin,
},
rules: {
'prettier/prettier': 'error',
},
},
];
36 changes: 27 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,32 @@
{
"name": "ethereum-scripts",
"version": "1.0.0",
"description": "Utility and helper scripts for working on ethereum smart-contracts",
"main": "index.js",
"license": "None",
"scripts": {
"deposit": "node deposit.js"
"name": "eclipse-deposit",
"version": "2.0.0",
"type": "module",
"description": "A tool for depositing Ether into the Eclipse rollup, supporting both Sepolia and Ethereum Mainnet.",
"main": "src/lib.js",
"bin": {
"eclipse-deposit": "bin/cli.js"
},
"repository": "https://github.com/Eclipse-Laboratories-Inc/eclipse-deposit",
"author": "Eclipse Labs Team",
"license": "Apache-2.0",
"private": false,
"dependencies": {
"bs58": "^5.0.0",
"ethers": "^6.8.0"
"@solana/web3.js": "^1.95.2",
"bs58": "^6.0.0",
"commander": "^12.1.0",
"viem": "^2.18.4"
},
"devDependencies": {
"@eslint/eslintrc": "^3.1.0",
"@eslint/js": "^9.8.0",
"eslint": "^9.8.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.2.1",
"prettier": "^3.3.3"
},
"scripts": {
"lint": "eslint 'src/**/*.js'",
"format": "prettier --write 'src/**/*.js'"
}
}
11 changes: 11 additions & 0 deletions src/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const { readFileSync } = require('fs');

function getPrivateKeyFromFile(filePath) {
try {
return readFileSync(filePath, 'utf8').trim();
} catch (error) {
throw new Error(`Failed to read private key from file: ${error.message}`);
}
}

module.exports = { getPrivateKeyFromFile };
19 changes: 19 additions & 0 deletions src/deposit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
async function deposit(client, etherBridgeAddress, destination, amountWei) {
try {
// Define the ABI for the EtherBridge contract
const abi = ['function deposit(bytes32,uint256) payable'];
const contract = new client.Contract(etherBridgeAddress, abi);

// Convert destination to bytes32 for compatibility
const destinationBytes32 = `0x${Buffer.from(destination).toString('hex').padEnd(64, '0')}`;

// Send the deposit transaction
const tx = await contract.deposit(destinationBytes32, amountWei);
console.log(`Transaction hash: ${tx.hash}`);
} catch (error) {
console.error(`Error during deposit: ${error.message}`);
throw error;
}
}

module.exports = deposit;
65 changes: 65 additions & 0 deletions src/lib.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
const { createClient, parseEther } = require('viem');
const { getPrivateKeyFromFile } = require('./config');
const { validateSolanaAddress } = require('./validate');
const deposit = require('./deposit');

// Define network configurations
const NETWORK_CONFIG = {
mainnet: {
etherBridgeAddress: '0xMainnetContractAddress',
rpcUrl: 'todo',
},
sepolia: {
etherBridgeAddress: '0xSepoliaContractAddress',
rpcUrl: 'https://rpc2.sepolia.org',
},
};

async function runDeposit({
keyFile,
destination,
amount,
mainnet,
sepolia,
rpcUrl,
}) {
try {
// Determine the network and validate
const network = mainnet ? 'mainnet' : sepolia ? 'sepolia' : null;
if (!network) {
throw new Error('Invalid network selection.');
}

// Validate and decode destination address
if (!validateSolanaAddress(destination)) {
throw new Error('Invalid Solana address.');
}

// Parse and validate the amount
const amountWei = parseEther(amount);

// Retrieve the private key from file
const privateKey = getPrivateKeyFromFile(keyFile);

// Get network-specific configuration
const { etherBridgeAddress, rpcUrl: defaultRpcUrl } =
NETWORK_CONFIG[network];
const effectiveRpcUrl = rpcUrl || defaultRpcUrl;

// Set up the client
const client = createClient({
transport: effectiveRpcUrl,
wallet: { privateKey },
});

// Call the deposit function with the validated inputs
await deposit(client, etherBridgeAddress, destination, amountWei);
} catch (error) {
console.error(`Error: ${error.message}`);
process.exit(1);
}
}

module.exports = {
runDeposit,
};
43 changes: 43 additions & 0 deletions src/validate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const bs58 = require('bs58');
const { PublicKey } = require('@solana/web3.js');

// List of known Solana native program addresses
const SOLANA_NATIVE_PROGRAMS = new Set([
'11111111111111111111111111111111', // System Program
'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA', // SPL Token Program
'Stake11111111111111111111111111111111111111', // Stake Program
// Add other known native programs as needed
]);

// Function to validate Solana address
function validateSolanaAddress(address) {
try {
// Decode the base58 address
const decoded = bs58.decode(address);

// Check the length to ensure it's a valid Ed25519 public key
if (decoded.length !== 32) {
throw new Error('Invalid length for a Solana address.');
}

// Convert the decoded address to a PublicKey object
const publicKey = new PublicKey(decoded);

// Check if the address is a known native program
if (SOLANA_NATIVE_PROGRAMS.has(publicKey.toString())) {
throw new Error('Address is a reserved Solana native program.');
}

// Additional check using Solana Web3's PublicKey class for verification
if (!PublicKey.isOnCurve(publicKey.toBuffer())) {
throw new Error('Address is not a valid Ed25519 public key.');
}

return true;
} catch (error) {
console.error(`Validation error: ${error.message}`);
return false;
}
}

module.exports = { validateSolanaAddress };
Loading

0 comments on commit 97cb2e5

Please sign in to comment.