Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
whonion committed Sep 17, 2023
0 parents commit 52a3eb1
Show file tree
Hide file tree
Showing 13 changed files with 1,266 additions and 0 deletions.
15 changes: 15 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
ETH_SEPOLIA_RPC = 'https://endpoints.omniatech.io/v1/eth/sepolia/public' # 11155111
ARB_SEPOLIA_RPC = 'https://sepolia-rollup.arbitrum.io/rpc' # 421614
STYLUS_TESTNET_RPC = 'https://stylus-testnet.arbitrum.io/rpc' # 23011913

# Deposit ETH from Sepolia to Arbitrum Sepolia
ARB_SEPOLIA_INBOX_ROUTER = '0xaAe29B0366299461418F5324a79Afc425BE5ae21'
# Deposit ETH from Arbitrum Sepolia to Arbitrum Stylus Test
ARB_STYLUS_INBOX_ROUTER = '0xe1e3b1CBaCC870cb6e5F4Bdf246feB6eB5cD351B'
# Contact address for mint Omnibase nft to Arbitrum Stylus Testnet
STYLUS_OMNIBASE_CONTRACT = '0xA02573c4Ad15c16b48f10842aaC9c9eA405B65A3'


EXP_ETH_SEPOLIA = 'https://sepolia.etherscan.io'
EXP_ARB_SEPOLIA = 'https://sepolia-explorer.arbitrum.io'
EXP_STL_ARBITRUM = 'https://stylus-testnet-explorer.arbitrum.io'
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto
Binary file added .github/preview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 29 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: TypeScript Build

on:
push:
branches:
- main

jobs:
build:
runs-on: ubuntu-latest

steps:
- name: Checkout Repository
uses: actions/checkout@v3

- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: 20

- name: Install Dependencies
run: npm i

- name: Set Global TypeScript
run: npm i -g typescript

- name: Build TS
run: tsc --project tsconfig.json

18 changes: 18 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Private files
seeds.txt
private_keys.txt
proxies.txt
.env
# Compilled js
*.js
!_*.js
# Dependency directories
node_modules/
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
test.ts
test.ts
32 changes: 32 additions & 0 deletions README.MD
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
[![Status](https://img.shields.io/badge/status-active-success.svg)](https://github.com/whonion/ts-stylus-bridger-and-minter/blob/main/) [![Build TS](https://github.com/whonion/ts-stylus-bridger-and-minter/actions/workflows/build.yml/badge.svg)](https://github.com/whonion/ts-stylus-bridger-and-minter/actions/workflows/test.yml) ![Node Version](https://img.shields.io/badge/Node.js-20.6.1-blue.svg) ![Ethers Version](https://img.shields.io/badge/ethers-5.7.2-red.svg) [![HitCount](https://hits.dwyl.com/whonion/ts-stylus-bridger-and-minter.svg)](https://hits.dwyl.com/whonion/ts-stylus-bridger-and-minter)</br>
## arbitrum-stylus-bridger-and-minter
TypeScript impelementation of Arbitrum Stylus Testnet Bridger and Omnibase NFT minter

`main.js` preview <br>![ts-stylus-bridger-and-minter](https://github.com/whonion/ts-stylus-bridger-and-minter/blob/main/.github/preview.png?raw=true)<br>

### Install `Node.js`
```sh
sudo apt install git
sudo apt install nodejs
sudo apt install npm
node -v
npm -v
```
### Clone repo and install dependencies
```sh
git clone https://github.com/whonion/ts-stylus-bridger-and-minter.git
cd ts-stylus-bridger-and-minter
npm i
npm i -g typescript
tsc --project tsconfig.json

```
## What's do script ?
### Bridge Funds
- Bridge Funds from Sepolia Testnet to Arbitrum Sepolia via [bridge.arbitrum.io](https://bridge.arbitrum.io)
- Bridge Funds from Arbitrum Sepolia to Arbitrum Stylus Testnet via [bridge.arbitrum.io](https://bridge.arbitrum.io)
- Mint Omnibase NFT (Powered by Layer Zero) to Arbitrum Stylus
## Run Script
```sh
node main.js
```
104 changes: 104 additions & 0 deletions bridge.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { ethers, BigNumber } from 'ethers';
import dotenv from 'dotenv';
import fs from 'fs';

dotenv.config();
const contractABI = [
{
name: "depositEth",
outputs: [],
payable: true,
stateMutability: "payable",
type: "function",
},
];
// Load private keys from private_keys.txt
const privateKeys = fs.readFileSync('private_keys.txt', 'utf-8').split('\n').map(key => key.trim()).filter(Boolean);

// Ethereum RPC and contract addresses with default values
const ethRpc = process.env.ETH_SEPOLIA_RPC || 'https://endpoints.omniatech.io/v1/eth/sepolia/public'; // Default RPC URL
const ethContractAddress = process.env.ARB_SEPOLIA_INBOX_ROUTER || '0xaAe29B0366299461418F5324a79Afc425BE5ae21'; // Default contract address

// Arbitrum RPC and contract addresses with default values
const arbRpc = process.env.ARB_SEPOLIA_RPC || 'https://sepolia-rollup.arbitrum.io/rpc'; // Default RPC URL
const arbContractAddress = process.env.ARB_STYLUS_INBOX_ROUTER || '0xe1e3b1CBaCC870cb6e5F4Bdf246feB6eB5cD351B'; // Default contract address

// Ethereum Sepolia explorer URL
const EXP_ETH_SEPOLIA = process.env.EXP_ETH_SEPOLIA || 'https://sepolia.etherscan.io'; // Default Arbitrum Sepolia Explorer URL
// Arbitrum Sepolia explorer URL
const EXP_ARB_SEPOLIA = process.env.EXP_ARB_SEPOLIA || 'https://sepolia-explorer.arbitrum.io'; // Default Arbitrum Sepolia Explorer URL

// Define gasLimit, maxFeePerGas, and maxPriorityFeePerGas here
const gasLimit = ethers.BigNumber.from('120000');
const maxFeePerGas = ethers.BigNumber.from('3000000000');
const maxPriorityFeePerGas = ethers.BigNumber.from('250000000');

// Function to send ETH to the contract using the depositEth function
async function sendEthToContract(privateKey: string, rpcUrl: string, ContractAddress: string, sendAmount: ethers.BigNumber, exp_url: string) {
// Create a provider with the provided RPC URL
const ethProvider = new ethers.providers.JsonRpcProvider(rpcUrl);

const wallet = new ethers.Wallet(privateKey, ethProvider);

try {
// Corrected ABI to call the 'depositEth' function
const contract = new ethers.Contract(ContractAddress, contractABI, wallet);

const nonce = await wallet.getTransactionCount();

// Transaction data
const tx = await contract.depositEth({
value: sendAmount, // Send ETH amount as a BigNumber
gasLimit: gasLimit, // Gas limit as a BigNumber
maxFeePerGas: maxFeePerGas, // Max fee per gas as a BigNumber
maxPriorityFeePerGas: maxPriorityFeePerGas, // Max priority fee per gas as a BigNumber
nonce: nonce, // Nonce as a regular number

});

// Wait for the transaction to be mined
const receipt = await tx.wait();

if (receipt.status === 0) {
// Transaction failed (reverted)
console.error(`⛔ Transaction reverted. Error message:\n ${receipt.logs[0]?.data}`);
} else {
console.log(`Sent ${ethers.utils.formatEther(sendAmount)} ETH from ${wallet.address} to the contract: 📃 ${ContractAddress} `);
console.log(`View on Blockchain explorer: ${exp_url}/tx/${tx.hash}`);
if (exp_url === EXP_ETH_SEPOLIA) {
console.log("✅ Successfully Bridged to Arbitrum Sepolia");
} else {
console.log("✅ Successfully Bridged to Arbitrum Stylus");
}
}
} catch (error) {
console.error(`❌ Error sending ETH from address ${wallet.address} to the contract: 📃 ${ContractAddress}`);
}

}
// Function to sleep for a specified duration in milliseconds
function sleep(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
export async function sendEth(ethValue: BigNumber) {
for (const privateKey of privateKeys) {
let explorer: string = EXP_ETH_SEPOLIA;
await sendEthToContract(privateKey, ethRpc, ethContractAddress, ethValue, explorer).catch((error) => {
console.error('❌ Error bridge ETH via contract: 📃 ${contractAddress}', error);
});
}

// Wait for the first cycle to complete before starting the second one with a delay of 6000 ms
await new Promise((resolve) => setTimeout(resolve, 6000));

for (const privateKey of privateKeys) {
let explorer: string = EXP_ARB_SEPOLIA;
await sendEthToContract(privateKey, arbRpc, arbContractAddress, ethValue, explorer).catch((error) => {
console.error('❌ Error sending ETH to the contract: 📃 ${contractAddress}', error);
});
}
}



//sendEth();
74 changes: 74 additions & 0 deletions main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { ethers, BigNumber } from 'ethers';
import { sendEth } from './bridge.js';
import { mintNFTs } from './mint.js';
import readline from 'readline';

const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});

async function displayMenu() {
console.log("Select action:");
console.log("1. Bridge ETH from Ethereum Sepolia to Arbitrum Sepolia and next Bridge to Arbitrum Stylus Testnet");
console.log("2. Mint Omnibase NFT to Arbitrum Stylus Test");
console.log("0. Exit");
}

async function performAction(action: number) {
switch (action) {
case 1:
rl.question("How many ETH do you want to send? ", async (amountETH) => {
const ethValue = ethers.BigNumber.from(ethers.utils.parseEther(amountETH));
console.log("🌐 Starting the script to bridge ETH via https://bridge.arbitrum.io");
await sendEth(ethValue);
console.log("ETH bridging complete.");
continueMenu();
});
break;
case 2:
rl.question("How many NFTs do you want to mint? ", async (count) => {
const mintAmount = ethers.BigNumber.from(count);
console.log("🌐 Starting the script to mint NFT via https://power.omnibase.xyz/");
await mintNFTs(mintAmount);
console.log("NFT minting complete.");
continueMenu();
});
break;
case 0:
console.log("Exiting...");
rl.close();
break;
default:
console.log("Invalid selection. Please enter the correct action.");
continueMenu();
break;
}
}

async function continueMenu() {
await displayMenu();

rl.question("Enter your choice: ", (choice) => {
const action = parseInt(choice);

if (isNaN(action)) {
console.log("Incorrect choice. Please enter the correct number.");
continueMenu();
} else {
if (action === 0) {
console.log("Exiting...");
rl.close();
} else {
performAction(action);
}
}
});
}

async function startMenu() {
await continueMenu();
}

// Start menu
startMenu();
97 changes: 97 additions & 0 deletions mint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { BigNumber, ethers } from 'ethers';
import dotenv from 'dotenv';
import fs from 'fs';

dotenv.config();

const mintABI = [
{
type: "function",
stateMutability: "payable",
outputs: [],
name: "mint",
inputs: [
{
type: "uint256",
name: "_mintAmount",
internalType: "uint256",
},
],
},
];

// Load private keys from private_keys.txt
const privateKeys = fs.readFileSync('private_keys.txt', 'utf-8').split('\n').map(key => key.trim()).filter(Boolean);
// Arbitrum Stylus Testnet RPC and contract addresses with default values
const stylusRpc = process.env.STYLUS_TESTNET_RPC || 'https://stylus-testnet.arbitrum.io/rpc'; // Default RPC URL
const stylusContractAddress = process.env.STYLUS_OMNIBASE_CONTRACT || '0xA02573c4Ad15c16b48f10842aaC9c9eA405B65A3'; // Default contract address
// Arbitrum Stylus explorer URL
const EXP_STL_ARBITRUM = process.env.EXP_STL_ARBITRUM || 'https://stylus-testnet-explorer.arbitrum.io'; // Default Arbitrum Sepolia Explorer URL



let exp_url = EXP_STL_ARBITRUM
// Function to send ETH to the contract using the mint function
async function mintNFT(privateKey: string, rpcUrl: string, contractAddress: string, mintAmount: ethers.BigNumber) {
// Create a provider with the provided RPC URL
const ethProvider = new ethers.providers.JsonRpcProvider(rpcUrl);

const wallet = new ethers.Wallet(privateKey, ethProvider);

try {
// Create a contract instance with the mint ABI
const contract = new ethers.Contract(contractAddress, mintABI, wallet);

const nonce = await wallet.getTransactionCount();

// Define gasLimit, maxFeePerGas, and maxPriorityFeePerGas mint cost here
const maxFeePerGas = ethers.BigNumber.from('1650000000'); // 1.62 Gwei
const maxPriorityFeePerGas = ethers.BigNumber.from('1000000000'); // 1 Gwei
const gasLimit = ethers.BigNumber.from('800000'); // Adjust as needed
const additionalValue = ethers.utils.parseEther('0.00023'); // Additional value in ETH
// Calculate the total value to send (including gas fees)
const gasFee = maxFeePerGas.mul(gasLimit);
const priorityFee = maxPriorityFeePerGas.mul(gasLimit);
const totalValue = gasFee.add(priorityFee).add(additionalValue);
//const mintAmount = ethers.BigNumber.from('1'); // Set count of NFT
const tx = await contract.mint(mintAmount,{
value: ethers.BigNumber.from(totalValue), // Send the total value
gasLimit: gasLimit,
maxFeePerGas: maxFeePerGas, // Set gas price here
maxPriorityFeePerGas: maxPriorityFeePerGas,
nonce: nonce,
});

const receipt = await tx.wait();

if (receipt.status === 0) {
// Transaction failed (reverted)
console.error(`⛔ Transaction reverted. Error message:\n ${receipt.logs[0]?.data}`);
} else {
console.log(`✅ Successfuly minted ${ethers.utils.formatUnits(mintAmount, 0)} NFTs from ${wallet.address} to the contract: 📃 ${contractAddress}`);

console.log(`View on Blockchain explorer: ${exp_url}/tx/${tx.hash}`);
}
} catch (error) {
console.error(`❌ Error minting NFT from address ${wallet.address} to the contract: 📃 ${contractAddress}\n`,error);
}
}

// Function to sleep for a specified duration in milliseconds
function sleep(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}

//const mintAmount = ethers.BigNumber.from('1'); // Set count of NFT

// Call the function to mint NFTs
export async function mintNFTs(mintAmount:BigNumber) {
for (const privateKey of privateKeys) {
await mintNFT(privateKey, stylusRpc, stylusContractAddress, mintAmount).catch((error) => {
console.error('❌ Error minting NFT:', error);
});
}

sleep(5000);
}
//mintNFTs();
Loading

0 comments on commit 52a3eb1

Please sign in to comment.