diff --git a/app/Components/About.tsx b/app/Components/About.tsx
index 966a56c..e8a9f99 100644
--- a/app/Components/About.tsx
+++ b/app/Components/About.tsx
@@ -1,8 +1,22 @@
import { Box, Card, CardContent, Typography, Link, Button, Avatar, Stack } from "@mui/material";
import GitHubIcon from "@mui/icons-material/GitHub";
+import GetAppIcon from "@mui/icons-material/GetApp";
+
+const WIREGUARD_COLOR = "#88171A";
+
+function WireGuardLogo({ size = 40 }: { size?: number }) {
+ return (
+
+ );
+}
export default function About() {
const repo = "https://github.com/cochrane2063/MSBD5017-Depin-WebClient";
+ const wireguard = "https://www.wireguard.com/install/";
return (
@@ -32,6 +46,59 @@ export default function About() {
+
+
+
+
+
+
+
+
+ Download Wireguard Client
+
+
+
+
+ Wireguard client for the MSBD5017 Depin project.
+
+
+
+ This project uses the Wireguard VPN protocol. You can download the Wireguard client from the official website.
+
+
+
+
+ }
+ variant="contained"
+ sx={{
+ bgcolor: WIREGUARD_COLOR,
+ "&:hover": { bgcolor: "#65a836" },
+ }}
+ >
+ Download WireGuard
+
+
+
+
+
+
+
);
}
\ No newline at end of file
diff --git a/app/Components/Contracts/CLRToken/CLRToken.json b/app/Components/Contracts/CLRToken/CLRToken.json
new file mode 100644
index 0000000..b3d59a7
--- /dev/null
+++ b/app/Components/Contracts/CLRToken/CLRToken.json
@@ -0,0 +1,420 @@
+[
+ {
+ "inputs": [],
+ "stateMutability": "nonpayable",
+ "type": "constructor"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "spender",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "allowance",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "needed",
+ "type": "uint256"
+ }
+ ],
+ "name": "ERC20InsufficientAllowance",
+ "type": "error"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "sender",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "balance",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "needed",
+ "type": "uint256"
+ }
+ ],
+ "name": "ERC20InsufficientBalance",
+ "type": "error"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "approver",
+ "type": "address"
+ }
+ ],
+ "name": "ERC20InvalidApprover",
+ "type": "error"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "receiver",
+ "type": "address"
+ }
+ ],
+ "name": "ERC20InvalidReceiver",
+ "type": "error"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "sender",
+ "type": "address"
+ }
+ ],
+ "name": "ERC20InvalidSender",
+ "type": "error"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "spender",
+ "type": "address"
+ }
+ ],
+ "name": "ERC20InvalidSpender",
+ "type": "error"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "owner",
+ "type": "address"
+ }
+ ],
+ "name": "OwnableInvalidOwner",
+ "type": "error"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "account",
+ "type": "address"
+ }
+ ],
+ "name": "OwnableUnauthorizedAccount",
+ "type": "error"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "owner",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "spender",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "value",
+ "type": "uint256"
+ }
+ ],
+ "name": "Approval",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "previousOwner",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "newOwner",
+ "type": "address"
+ }
+ ],
+ "name": "OwnershipTransferred",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "from",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "to",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "value",
+ "type": "uint256"
+ }
+ ],
+ "name": "Transfer",
+ "type": "event"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "owner",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "spender",
+ "type": "address"
+ }
+ ],
+ "name": "allowance",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "spender",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "value",
+ "type": "uint256"
+ }
+ ],
+ "name": "approve",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "account",
+ "type": "address"
+ }
+ ],
+ "name": "balanceOf",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "_amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "burn",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "decimals",
+ "outputs": [
+ {
+ "internalType": "uint8",
+ "name": "",
+ "type": "uint8"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "_to",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "_amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "mint",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "name",
+ "outputs": [
+ {
+ "internalType": "string",
+ "name": "",
+ "type": "string"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "owner",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "renounceOwnership",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "symbol",
+ "outputs": [
+ {
+ "internalType": "string",
+ "name": "",
+ "type": "string"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "totalSupply",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "to",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "value",
+ "type": "uint256"
+ }
+ ],
+ "name": "transfer",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "from",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "to",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "value",
+ "type": "uint256"
+ }
+ ],
+ "name": "transferFrom",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "newOwner",
+ "type": "address"
+ }
+ ],
+ "name": "transferOwnership",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ }
+]
\ No newline at end of file
diff --git a/app/Components/Contracts/ClearNet.json b/app/Components/Contracts/ClearNet/ClearNet.json
similarity index 95%
rename from app/Components/Contracts/ClearNet.json
rename to app/Components/Contracts/ClearNet/ClearNet.json
index bf704db..ecb3f88 100644
--- a/app/Components/Contracts/ClearNet.json
+++ b/app/Components/Contracts/ClearNet/ClearNet.json
@@ -615,6 +615,25 @@
"stateMutability": "nonpayable",
"type": "function"
},
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "name": "activeChannelIds",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
{
"inputs": [
{
@@ -740,6 +759,48 @@
"stateMutability": "view",
"type": "function"
},
+ {
+ "inputs": [],
+ "name": "getActivePaymentChannels",
+ "outputs": [
+ {
+ "internalType": "address[]",
+ "name": "",
+ "type": "address[]"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "_offset",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "_limit",
+ "type": "uint256"
+ }
+ ],
+ "name": "getActivePaymentChannelsPaginated",
+ "outputs": [
+ {
+ "internalType": "address[]",
+ "name": "channels",
+ "type": "address[]"
+ },
+ {
+ "internalType": "uint256",
+ "name": "total",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
{
"inputs": [
{
diff --git a/app/Components/Contracts/Connections.tsx b/app/Components/Contracts/Connections.tsx
index ecd10ce..7ef175c 100644
--- a/app/Components/Contracts/Connections.tsx
+++ b/app/Components/Contracts/Connections.tsx
@@ -1,28 +1,87 @@
import { Web3 } from 'web3';
-import clearNetJson from './ClearNet.json';
+import type { Node, NodeInfo, PaymentChannelInfo } from '../Util';
+import clearNetJson from './ClearNet/ClearNet.json';
+import clrTokenJson from './CLRTOKEN/CLRToken.json';
const clearNetABI = (clearNetJson as any).abi ?? clearNetJson;
-const clearNetAddress = "0xf04cbb756045b276ea962ea98d938a0ed8101f51";
+const clrTokenABI = (clrTokenJson as any).abi ?? clrTokenJson;
+const clearNetAddress = "0xb6f537b38b82d08ff3ed796754d9d85b5cfe9cb5";
+const clrTokenAddress = "0xf1664c17887767c8f58695846babb349ca61d2e9";
+const DEFAULT_MIN_STAKE = BigInt(10000) * BigInt(1e18); // 10000 CLR
-export const getActiveNodes = async (provider: any) => {
+
+export const getActiveNodes = async (provider: any): Promise => {
+ if (!provider) {
+ return [];
+ }
const web3 = new Web3(provider);
- const contract = new web3.eth.Contract(clearNetABI, clearNetAddress);
- const activeNodes = await contract.methods.getActiveNodes().call();
- return activeNodes;
+ const clearnet_contract = new web3.eth.Contract(clearNetABI, clearNetAddress);
+ const activeNodes: string[] = await clearnet_contract.methods.getActiveNodes().call();
+ const nodes: Node[] = await Promise.all(activeNodes.map(async (node: string) => {
+ const nodeInfo: NodeInfo = await clearnet_contract.methods.getNodeInfo(node).call();
+ return {
+ ip: nodeInfo.ipAddress,
+ port: Number(nodeInfo.port),
+ traffic: 0,
+ price: Number(nodeInfo.pricePerMinute) / 1e18,
+ rating: Number(nodeInfo.reputationScore) / 1000,
+ };
+ }));
+ console.log("Fetched nodes:", nodes);
+ return nodes;
}
-export async function registerNode(provider: any,account: string) {
+export async function approveCLRTokenSpending(provider: any,account: string) {
const web3 = new Web3(provider);
- const contract = new web3.eth.Contract(clearNetABI, clearNetAddress);
+ const clr_token_contract = new web3.eth.Contract(clrTokenABI, clrTokenAddress);
const gasPrice = await web3.eth.getGasPrice();
const gasLimit = 300000;
- const tx = await contract.methods.registerNode("57.158.82.48", 51820, 1000000000000000000n).send({
+ const tx = await clr_token_contract.methods.approve(clearNetAddress, DEFAULT_MIN_STAKE).send({
+ from: account,
+ gas: String(gasLimit),
+ gasPrice: String(gasPrice),
+ });
+ console.log(tx);
+}
+
+export async function openPaymentChannel(provider: any,account: string,amount: BigInt) {
+ const web3 = new Web3(provider);
+ const clearnet_contract = new web3.eth.Contract(clearNetABI, clearNetAddress);
+ const gasPrice = await web3.eth.getGasPrice();
+ const gasLimit = 300000;
+ const tx = await clearnet_contract.methods.openPaymentChannel(amount).send({
from: account,
gas: String(gasLimit),
gasPrice: String(gasPrice),
});
console.log(tx);
+}
+export async function closePaymentChannel(provider: any,account: string) {
+ const web3 = new Web3(provider);
+ const clearnet_contract = new web3.eth.Contract(clearNetABI, clearNetAddress);
+ const gasPrice = await web3.eth.getGasPrice();
+ const gasLimit = 300000;
+ const tx = await clearnet_contract.methods.closePaymentChannel().send({
+ from: account,
+ gas: String(gasLimit),
+ gasPrice: String(gasPrice),
+ });
+ console.log(tx);
+}
+
+export async function getPaymentChannelInfo(provider: any,account: string) : Promise {
+ if (!provider) {
+ return { balance: BigInt(0), nonce: BigInt(0), isActive: false};
+ }
+ const web3 = new Web3(provider);
+ const clearnet_contract = new web3.eth.Contract(clearNetABI, clearNetAddress);
+ const channelInfo: PaymentChannelInfo = await clearnet_contract.methods.getPaymentChannelInfo(account).call();
+ return {
+ balance: channelInfo.balance,
+ nonce: channelInfo.nonce,
+ isActive: channelInfo.isActive,
+ };
}
\ No newline at end of file
diff --git a/app/Components/Metamask/Connections.tsx b/app/Components/Metamask/Connections.tsx
index 7324b70..25f5275 100644
--- a/app/Components/Metamask/Connections.tsx
+++ b/app/Components/Metamask/Connections.tsx
@@ -16,8 +16,6 @@ export const signMessage = async (message: string,provider: EIP1193Provider,user
// For historical reasons, you must submit the message to sign in hex-encoded UTF-8.
// This uses a Node.js-style buffer shim in the browser.
- // const bytes = new TextEncoder().encode(String(publicKey));
- // const msg = '0x' + Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');
const msg = `0x${Buffer.from(message, "utf8").toString("hex")}`
const sig = await provider.request({
method: "personal_sign",
diff --git a/app/Components/Metamask/DiscoverWalletProviders.tsx b/app/Components/Metamask/DiscoverWalletProviders.tsx
index b65052c..3cc7367 100644
--- a/app/Components/Metamask/DiscoverWalletProviders.tsx
+++ b/app/Components/Metamask/DiscoverWalletProviders.tsx
@@ -1,4 +1,3 @@
-// ...existing code...
import React, { useState } from "react";
import { useSyncProviders } from "./useSyncProviders";
import useAuth from "~/hooks/useAuth";
@@ -122,5 +121,4 @@ export const DiscoverWalletProviders: React.FC = () => {
);
};
-export default DiscoverWalletProviders;
-// ...existing code...
\ No newline at end of file
+export default DiscoverWalletProviders;
\ No newline at end of file
diff --git a/app/Components/NavBar.tsx b/app/Components/NavBar.tsx
index 876f982..44b9dbc 100644
--- a/app/Components/NavBar.tsx
+++ b/app/Components/NavBar.tsx
@@ -1,4 +1,4 @@
-import React from "react";
+import React, { useEffect } from "react";
import { NavLink } from "react-router";
import AppBar from '@mui/material/AppBar';
import Box from '@mui/material/Box';
@@ -7,13 +7,16 @@ import Button from "@mui/material/Button";
import LoginIcon from "@mui/icons-material/Login";
import { Typography, Menu, MenuItem, IconButton, Avatar, Tooltip } from "@mui/material";
import useAuth from "~/hooks/useAuth";
-import { signMessage } from './Metamask/Connections';
+import type { PaymentChannelInfo } from './Util';
+import { approveCLRTokenSpending, openPaymentChannel, closePaymentChannel, getPaymentChannelInfo } from './Contracts/Connections';
export default function ButtonAppBar() {
const { auth, setAuth } = useAuth();
const account = auth?.accounts?.[0];
const isAuthed = Boolean(account);
+ const [paymentChannelInfo, setPaymentChannelInfo] = React.useState(null);
+ const [registered, setRegistered] = React.useState(false);
const [anchorEl, setAnchorEl] = React.useState(null);
const open = Boolean(anchorEl);
const handleOpen = (e: React.MouseEvent) => setAnchorEl(e.currentTarget);
@@ -27,14 +30,28 @@ export default function ButtonAppBar() {
};
const register = async () => {
- const sig = await signMessage("H8zfXnSclIQ/wLy7GSt7GNqa1utAi4Uvr7Dg3p9vdHQ=",auth.providerWithInfo.provider, auth.accounts[0]);
- try {
- const res_string = sig;
- } catch (error) {
- console.error('Error:', error);
- }
+ const clr_deposit_amount = BigInt(10000) * BigInt(1e18); // 10000 CLR
+ await approveCLRTokenSpending(auth.providerWithInfo.provider, account!);
+ await openPaymentChannel(auth.providerWithInfo.provider, account!, clr_deposit_amount);
+ setRegistered(true);
}
+ const deRegister = async () => {
+ await closePaymentChannel(auth.providerWithInfo.provider, account!);
+ setRegistered(false);
+ }
+
+ useEffect(() => {
+ getPaymentChannelInfo(auth.providerWithInfo ? auth.providerWithInfo.provider : undefined, account!).then((info) => {
+ setPaymentChannelInfo(info);
+ if(registered !== info.isActive) {
+ setRegistered(info.isActive);
+ }
+ console.log("Payment Channel Info:", info);
+ });
+ }, [account, auth.providerWithInfo?.provider,registered]);
+
+
return (
@@ -78,8 +95,7 @@ export default function ButtonAppBar() {
Login
)}
-
- {}
+
{isAuthed && (
<>
@@ -105,7 +121,7 @@ export default function ButtonAppBar() {
transformOrigin={{ horizontal: 'right', vertical: 'top' }}
anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }}
>
-
+ {(paymentChannelInfo && paymentChannelInfo.isActive) ? () : ()}
>
diff --git a/app/Components/ServerList.tsx b/app/Components/ServerList.tsx
index 3298c30..6a22add 100644
--- a/app/Components/ServerList.tsx
+++ b/app/Components/ServerList.tsx
@@ -2,27 +2,41 @@ import * as React from 'react';
import ButtonGroup from '@mui/material/ButtonGroup';
import Button from '@mui/material/Button';
import Avatar from '@mui/material/Avatar';
-import { Grid, Card, CardContent, CardActions, Typography, Box, Chip, Dialog, DialogTitle, DialogContent, DialogActions, TextField } from "@mui/material";
+import {
+ Grid,
+ Card,
+ CardContent,
+ CardActions,
+ Typography,
+ Box,
+ Chip,
+ Dialog,
+ DialogTitle,
+ DialogContent,
+ DialogActions,
+ IconButton,
+ Tooltip,
+ CircularProgress,
+} from "@mui/material";
import ServerIcon from '@mui/icons-material/Dns';
import SignalCellularAltIcon from '@mui/icons-material/SignalCellularAlt';
import AttachMoneyIcon from '@mui/icons-material/AttachMoney';
import StarIcon from '@mui/icons-material/Star';
import Rating from '@mui/material/Rating';
+import RefreshIcon from '@mui/icons-material/Refresh';
import useAuth from '~/hooks/useAuth';
+import type { AccountInfo } from '~/context/AuthProvider';
import { signMessage } from './Metamask/Connections';
-import { downloadWireguardConfig } from './WireguardConfig';
+import { generateWireguardKeyPair, downloadWireguardConfig } from './WireguardConfig';
+import type { Node } from './Util';
+import { getActiveNodes } from './Contracts/Connections';
import axios from 'axios';
+import { useEffect } from 'react';
-interface Node {
- ip: string;
- traffic: number;
- price : number;
- rating : number;
-}
-function NodeItem({node}: {node: Node}) {
+
+function NodeItem({node, auth}: {node: Node, auth: AccountInfo}) {
const port = 8080;
- const { auth } = useAuth();
const [ratingOpen, setRatingOpen] = React.useState(false);
const [ratingValue, setRatingValue] = React.useState(null);
const [submittingRating, setSubmittingRating] = React.useState(false);
@@ -31,13 +45,16 @@ function NodeItem({node}: {node: Node}) {
// const publicKey = await getPublicKey(auth.providerWithInfo.provider, auth.accounts[0]);
try {
const iv = await axios.get(getUrl() + "/connect");
- console.log(iv.data);
+ const { privatekey: clientPrivateKey, publicKey: clientPublicKey } = generateWireguardKeyPair();
- const sig = await signMessage(iv.data + "H8zfXnSclIQ/wLy7GSt7GNqa1utAi4Uvr7Dg3p9vdHQ=",auth.providerWithInfo.provider, auth.accounts[0]);
- const res_string = iv.data + '\n' + "H8zfXnSclIQ/wLy7GSt7GNqa1utAi4Uvr7Dg3p9vdHQ=" + '\n' + sig;
+ const sig = await signMessage(iv.data + clientPublicKey,auth.providerWithInfo.provider, auth.accounts[0]);
+ const res_string = iv.data + '\n' + clientPublicKey + '\n' + sig;
let response = await axios.post(getUrl() + "/connect", res_string);
+ const clientCIDR = response.data.WireguardClientCIDR;
+ const serverPublicKey = response.data.WireguardServerPublicKey;
+ const dns = response.data.WireguardDNS;
console.log(response.data);
- downloadWireguardConfig("", "", "", "", node.ip, "51820", "0.0.0.0/0");
+ downloadWireguardConfig(clientPrivateKey, serverPublicKey, clientCIDR, dns, node.ip, String(node.port), "0.0.0.0/0");
} catch (error) {
console.error('Error:', error);
}
@@ -180,33 +197,69 @@ function NodeItem({node}: {node: Node}) {
}
export default function FolderList() {
- const [nodes, setNodes] = React.useState([
- { ip: "57.158.82.48", traffic: 5, price: 10, rating: 3 },
- { ip: "8.210.33.199", traffic: 3, price: 15, rating: 4 },
- { ip: "45.77.12.5", traffic: 7, price: 20, rating: 5 },
- { ip: "203.120.45.78", traffic: 2, price: 8, rating: 2 },
- { ip: "91.189.88.25", traffic: 6, price: 12, rating: 4 },
- { ip: "132.148.9.201", traffic: 9, price: 18, rating: 5 },
- { ip: "60.12.180.99", traffic: 4, price: 14, rating: 3 },
- { ip: "199.59.243.100", traffic: 1, price: 6, rating: 1 },
- { ip: "34.216.77.3", traffic: 8, price: 22, rating: 5 },
- { ip: "185.199.108.153", traffic: 5, price: 11, rating: 4 },
- { ip: "13.107.21.200", traffic: 7, price: 16, rating: 4 },
- { ip: "216.58.214.14", traffic: 3, price: 9, rating: 2 },
- { ip: "104.21.44.33", traffic: 10, price: 25, rating: 5 },
- { ip: "47.90.12.201", traffic: 2, price: 7, rating: 1 },
- { ip: "23.45.67.89", traffic: 6, price: 13, rating: 3 },
- { ip: "192.0.2.123", traffic: 4, price: 17, rating: 4 },
- ]);
+ const { auth } = useAuth();
+ const [nodes, setNodes] = React.useState([]);
+ const [loading, setLoading] = React.useState(false);
+ const fetchNodes = React.useCallback(async () => {
+ setLoading(true);
+ try {
+ const provider = auth?.providerWithInfo?.provider;
+ const fetchedNodes = await getActiveNodes(provider);
+ setNodes(fetchedNodes);
+ } catch (err) {
+ console.error("Failed to fetch nodes", err);
+ } finally {
+ setLoading(false);
+ }
+ }, [auth]);
+
+ useEffect(() => {
+ fetchNodes();
+ }, [fetchNodes]);
+ // const [nodes, setNodes] = React.useState([
+ // { ip: "57.158.82.48", traffic: 5, price: 10, rating: 3 },
+ // { ip: "8.210.33.199", traffic: 3, price: 15, rating: 4 },
+ // { ip: "45.77.12.5", traffic: 7, price: 20, rating: 5 },
+ // { ip: "203.120.45.78", traffic: 2, price: 8, rating: 2 },
+ // { ip: "91.189.88.25", traffic: 6, price: 12, rating: 4 },
+ // { ip: "132.148.9.201", traffic: 9, price: 18, rating: 5 },
+ // { ip: "60.12.180.99", traffic: 4, price: 14, rating: 3 },
+ // { ip: "199.59.243.100", traffic: 1, price: 6, rating: 1 },
+ // { ip: "34.216.77.3", traffic: 8, price: 22, rating: 5 },
+ // { ip: "185.199.108.153", traffic: 5, price: 11, rating: 4 },
+ // { ip: "13.107.21.200", traffic: 7, price: 16, rating: 4 },
+ // { ip: "216.58.214.14", traffic: 3, price: 9, rating: 2 },
+ // { ip: "104.21.44.33", traffic: 10, price: 25, rating: 5 },
+ // { ip: "47.90.12.201", traffic: 2, price: 7, rating: 1 },
+ // { ip: "23.45.67.89", traffic: 6, price: 13, rating: 3 },
+ // { ip: "192.0.2.123", traffic: 4, price: 17, rating: 4 },
+ // ]);
return (
-
- Available Servers
-
+
+
+ Available Servers
+
+
+
+
+
+
+ {loading ? : }
+
+
+
+
+
{nodes.map((node, index) => (
-
+
))}
diff --git a/app/Components/Util.tsx b/app/Components/Util.tsx
new file mode 100644
index 0000000..ec3f248
--- /dev/null
+++ b/app/Components/Util.tsx
@@ -0,0 +1,23 @@
+
+export interface Node {
+ ip: string;
+ port: number;
+ traffic: number;
+ price : number;
+ rating : number;
+}
+
+export interface NodeInfo {
+ ipAddress: string;
+ port: number;
+ pricePerMinute : BigInt;
+ reputationScore : BigInt;
+ totalMinutesServed : BigInt;
+ totalEarnings : BigInt;
+}
+
+export interface PaymentChannelInfo {
+ balance: BigInt;
+ nonce: BigInt;
+ isActive: boolean;
+}
\ No newline at end of file
diff --git a/app/Components/WireguardConfig.tsx b/app/Components/WireguardConfig.tsx
index 638afcf..c01eea0 100644
--- a/app/Components/WireguardConfig.tsx
+++ b/app/Components/WireguardConfig.tsx
@@ -1,13 +1,57 @@
+// import { utils } from 'noble-ed25519';
+import nacl from "tweetnacl";
+function bytesToBase64(bytes: Uint8Array) {
+ let binary = '';
+ for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
+ return btoa(binary);
+}
+
+function base64ToBytes(base64: string): Uint8Array {
+ // support URL-safe base64
+ base64 = base64.replace(/-/g, "+").replace(/_/g, "/");
+ // pad to multiple of 4
+ const pad = base64.length % 4;
+ if (pad) base64 += "=".repeat(4 - pad);
+
+ const binary = atob(base64);
+ const len = binary.length;
+ const bytes = new Uint8Array(len);
+ for (let i = 0; i < len; i++) {
+ bytes[i] = binary.charCodeAt(i);
+ }
+ return bytes;
+}
+
+const generateWireGuardPrivateKey = () => {
+ // const privateKeyBytes = utils.randomPrivateKey();
+ // const privateKeyBase64 = bytesToBase64(privateKeyBytes);
+ const privateKey = nacl.randomBytes(32);
+
+ return bytesToBase64(privateKey);
+}
+
+const getWireguardPublicKey = (privateKey: string) => {
+ const privateKeyBytes = base64ToBytes(privateKey);
+ const publicKey = nacl.scalarMult.base(privateKeyBytes);
+ return bytesToBase64(publicKey);
+}
+
+export const generateWireguardKeyPair = () => {
+ const privatekey = generateWireGuardPrivateKey();
+ const publicKey = getWireguardPublicKey(privatekey);
+ return { privatekey, publicKey };
+}
+
+export const downloadWireguardConfig = async (clientPrivateKey: string, serverPubcliKey: string ,localCIDR: string, dns: string, peerIp: string, peerPort: string, allowedIPs: string) => {
-export const downloadWireguardConfig = (privateKey: string, publicKey: string, localCIDR: string, dns: string, peerIp: string, peerPort: string, allowedIPs: string) => {
const content = `[Interface]`+'\n'+
- `PrivateKey = ${privateKey}`+'\n'+
+ `PrivateKey = ${clientPrivateKey}`+'\n'+
`Address = ${localCIDR}`+'\n'+
`DNS = ${dns}`+'\n'+
'\n'+
`[Peer]`+'\n'+
- `PublicKey = ${publicKey}`+'\n'+
+ `PublicKey = ${serverPubcliKey}`+'\n'+
`AllowedIPs = ${allowedIPs}`+'\n'+
`Endpoint = ${peerIp}:${peerPort}`;
diff --git a/package-lock.json b/package-lock.json
index 4f7276c..f938e75 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14,9 +14,11 @@
"axios": "^1.13.2",
"buffer": "^6.0.3",
"isbot": "^5.1.31",
+ "noble-ed25519": "^1.2.6",
"react": "^19.1.1",
"react-dom": "^19.1.1",
"react-router": "^7.9.2",
+ "tweetnacl": "^1.0.3",
"web3": "^4.16.0"
},
"devDependencies": {
@@ -4513,6 +4515,13 @@
"node": ">= 0.6"
}
},
+ "node_modules/noble-ed25519": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/noble-ed25519/-/noble-ed25519-1.2.6.tgz",
+ "integrity": "sha512-zfnWqg9FVMp8CnzUpAjbt1nDXpDjCvxYiCXdnW1mY8zQHw/6twUlkFm14VPdojVzc0kcd+i9zT79+26GcNbsuQ==",
+ "deprecated": "Switch to namespaced @noble/ed25519 for security and feature updates",
+ "license": "MIT"
+ },
"node_modules/node-fetch": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
@@ -5676,6 +5685,12 @@
}
}
},
+ "node_modules/tweetnacl": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz",
+ "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==",
+ "license": "Unlicense"
+ },
"node_modules/type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
diff --git a/package.json b/package.json
index f0f6854..825c5bc 100644
--- a/package.json
+++ b/package.json
@@ -18,9 +18,11 @@
"axios": "^1.13.2",
"buffer": "^6.0.3",
"isbot": "^5.1.31",
+ "noble-ed25519": "^1.2.6",
"react": "^19.1.1",
"react-dom": "^19.1.1",
"react-router": "^7.9.2",
+ "tweetnacl": "^1.0.3",
"web3": "^4.16.0"
},
"devDependencies": {