Skip to content

Commit

Permalink
PRO-2110 - Unsafe Mode (#58)
Browse files Browse the repository at this point in the history
* added unsafe mode env and its functionalities

* updated readme file
  • Loading branch information
vignesha22 authored Jan 30, 2024
1 parent ace6bcf commit 32fb3a0
Show file tree
Hide file tree
Showing 27 changed files with 864 additions and 218 deletions.
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,30 @@ This would spin up three services at once which will be available on these urls:
- http://localhost:3000 (Arka Frontend for EndUser)
- http://localhost:5050 (Arka backend for Api service)

## Local Configuration Changes

There is an option to run the code locally without using AWS and only using local SQLite. These are the following steps to follow for using local SQLite database for apiKey and frontend to sync up

* Edit the docker-compose.yml to change the UNSAFE_MODE variable under backend to be true
* Run `docker compose up`
* Once its running, go to `http://localhost:3002/apiKey` and add the necessary apiKey and its private Key to store it to the local sqlite. Please Note that the privateKey will be stored in encrypted format with the mac address as the secret string for encryption and decryption process
* NOTE: The SUPPORTED_NETWORKS and ERC20_PAYMASTERS parameters require to input in base64 format and the original structure is described as follows
- SUPPORTED_NETWORKS - the networks you wish to support. The structure should follow this file config.json which again needs to be converted into `base64` value
- ERC20_PAYMASTERS - the custom deployed pimlico erc20 paymaster contract addresses. The structure should be as follows
{
"10": {
"USDC": "0x99fB8d618F52a42049776899D5c07241D344a8A4",
"DAI": "0x3bE5380ec8cfe159f0525d16d11E9Baba516C40c",
"USDT": "0x9102889001d0901b3d9123651d492e52ce772C6b"
},
"420": {
"LINK": "0x53F48579309f8dBfFE4edE921C50200861C2482a"
},
"421613": {
"LINK": "0x0a6Aa1Bd30D6954cA525315287AdeeEcbb6eFB59"
}
} which also needs to be converted into `base64` value


## 🔙 Arka Backend

Expand Down
2 changes: 2 additions & 0 deletions admin_frontend/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Route, Routes } from 'react-router-dom';
import { Toaster } from "react-hot-toast";
import NotFound from './components/NotFound';
import Dashboard from "./components/Dashboard";
import ApiKeysPage from "./components/ApiKeys";

// Css
import { ThemeProvider, createTheme } from '@mui/material/styles';
Expand All @@ -26,6 +27,7 @@ function App() {
<div className='mx-auto h-max' style={{ overflowY: 'auto', overflowX: 'auto' }}>
<Routes>
<Route path='/' element={<Dashboard />} />
<Route path='/apiKey' element={<ApiKeysPage />} />
<Route path='*' element={<NotFound />} />
</Routes>
</div>
Expand Down
275 changes: 275 additions & 0 deletions admin_frontend/src/components/ApiKeys.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,275 @@
import { useEffect, useState } from "react";
import AddCircleIcon from "@mui/icons-material/AddCircle";
import RemoveCircleIcon from '@mui/icons-material/RemoveCircle';
import toast from "react-hot-toast";
import { TextField } from "@mui/material";
import Table from "@mui/material/Table";
import TableBody from "@mui/material/TableBody";
import TableCell from "@mui/material/TableCell";
import TableContainer from "@mui/material/TableContainer";
import TableHead from "@mui/material/TableHead";
import TableRow from "@mui/material/TableRow";
import LoadingButton from "@mui/lab/LoadingButton";
import InputAdornment from "@mui/material/InputAdornment";
import Visibility from "@mui/icons-material/Visibility";
import VisibilityOff from "@mui/icons-material/VisibilityOff";
import IconButton from "@mui/material/IconButton";
import OutlinedInput from "@mui/material/OutlinedInput";
import FormControl from "@mui/material/FormControl";
import InputLabel from "@mui/material/InputLabel";
import Header from "./Header";

const ApiKeysPage = () => {
const [keys, setKeys] = useState([]);
const [loading, setLoading] = useState(false);
const [apiKey, setApiKey] = useState("");
const [privateKey, setPrivateKey] = useState("");
const [supportedNetworks, setSupportedNetworks] = useState("");
const [customErc20Paymaster, setCustomErc20Paymaster] = useState("");
const [showPassword, setShowPassword] = useState(false);

const handleClickShowPassword = () => setShowPassword(!showPassword);

const handleMouseDownPassword = (event) => {
event.preventDefault();
};

const fetchData = async () => {
try {
setLoading(true);
const data = await (
await fetch("http://localhost:5050/getKeys", {
method: "GET",
})
).json();
console.log("data: ", data);
setKeys(data);
setLoading(false);
} catch (err) {
toast.error(
"Check Backend Service for more info"
);
}
};

useEffect(() => {
fetchData();
}, []);

const handleSubmit = async () => {
if (apiKey === "" || privateKey === "") {
toast.error("Please input both API_KEY & PRIVATE_KEY field");
return;
}
try {
setLoading(true);
const requestData = {
API_KEY: apiKey,
PRIVATE_KEY: privateKey,
SUPPORTED_NETWORKS: supportedNetworks ?? "",
ERC20_PAYMASTERS: customErc20Paymaster ?? "",
};
const data = await (
await fetch("http://localhost:5050/saveKey", {
method: "POST",
body: JSON.stringify(requestData),
})
).json();
if (!data.error) {
toast.success("Saved Successfully");
setApiKey("");
setPrivateKey("");
fetchData();
} else {
setLoading(false);
toast.error("Could not save");
}
} catch (err) {
toast.error(
"Check Backend Service for more info"
);
setLoading(false);
}
};

const handleDelete = async (key) => {
try {
setLoading(true);
const data = await (
await fetch("http://localhost:5050/deleteKey", {
method: "POST",
body: JSON.stringify({ API_KEY: key }),
})
).json();
if (!data.error) {
toast.success("Deleted Successfully");
fetchData();
} else {
setLoading(false);
toast.error("Could not save");
}
} catch (err) {
console.log("err: ", err);
toast.error(
"Check Backend Service for more info"
);
setLoading(false);
}
};

return (
<>
<Header className="align-center" text="Arka Admin Api Keys" />
<TableContainer>
<Table aria-label="simple table" stickyHeader>
<TableHead>
<TableRow>
<TableCell>Wallet Address</TableCell>
<TableCell>Api Key</TableCell>
<TableCell>Private Key</TableCell>
<TableCell>Supported Networks</TableCell>
<TableCell>Custom ERC20 Paymasters</TableCell>
<TableCell>Actions Available</TableCell>
</TableRow>
</TableHead>
<TableBody>
<TableRow>
<TableCell>
<TextField
type="text"
variant="outlined"
color="secondary"
label="WALLET_ADDRESS"
value={"AutoFill"}
multiline
disabled
/>
</TableCell>
<TableCell>
<TextField
type="text"
variant="outlined"
color="secondary"
label="API_KEY"
onChange={(e) => setApiKey(e.target.value)}
value={apiKey}
required
multiline
fullWidth
/>
</TableCell>
<TableCell>
<FormControl variant="outlined" required fullWidth>
<InputLabel htmlFor="outlined-adornment-password">
PRIVATE_KEY
</InputLabel>
<OutlinedInput
id="outlined-adornment-password"
type={showPassword ? "text" : "password"}
endAdornment={
<InputAdornment position="end">
<IconButton
aria-label="toggle password visibility"
onClick={handleClickShowPassword}
onMouseDown={handleMouseDownPassword}
edge="end"
>
{showPassword ? <VisibilityOff /> : <Visibility />}
</IconButton>
</InputAdornment>
}
label="PRIVATE_KEY"
multiline
value={privateKey}
onChange={(e) => setPrivateKey(e.target.value)}
/>
</FormControl>
</TableCell>
<TableCell>
<TextField
type="text"
variant="outlined"
color="secondary"
label="SUPPORTED_NETWORKS"
onChange={(e) => setSupportedNetworks(e.target.value)}
value={supportedNetworks}
required
multiline
fullWidth
/>
</TableCell>
<TableCell>
<TextField
type="text"
variant="outlined"
color="secondary"
label="ERC20_PAYMASTERS"
onChange={(e) => setCustomErc20Paymaster(e.target.value)}
value={customErc20Paymaster}
required
multiline
fullWidth
/>
</TableCell>
<TableCell>
<LoadingButton
loading={loading}
disabled={loading}
loadingPosition="start"
startIcon={<AddCircleIcon />}
variant="contained"
onClick={() => {
handleSubmit();
}}
>
Add Row
</LoadingButton>
</TableCell>
</TableRow>
{keys.map((row) => (
<TableRow key={row.API_KEY}>
<TableCell>{row.WALLET_ADDRESS}</TableCell>
<TableCell>{row.API_KEY}</TableCell>
<TableCell>
<div className="flex flex-row">
<div>{showPassword ? row.PRIVATE_KEY : "*****"} </div>
<div>
<InputAdornment position="end">
<IconButton
aria-label="toggle password visibility"
onClick={handleClickShowPassword}
onMouseDown={handleMouseDownPassword}
edge="end"
>
{showPassword ? <VisibilityOff /> : <Visibility />}
</IconButton>
</InputAdornment>
</div>
</div>
</TableCell>
<TableCell>{row.SUPPORTED_NETWORKS}</TableCell>
<TableCell>{row.ERC20_PAYMASTERS}</TableCell>
<TableCell>
<LoadingButton
loading={loading}
disabled={loading}
loadingPosition="start"
startIcon={<RemoveCircleIcon />}
variant="contained"
onClick={() => {
handleDelete(row.API_KEY);
}}
>
Delete Row
</LoadingButton>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</>
);
};

export default ApiKeysPage;
6 changes: 3 additions & 3 deletions admin_frontend/src/components/Dashboard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ const Dashboard = () => {
setLoading(false);
} catch (err) {
toast.error(
"Make sure that backend server is running since its unreachable"
"Check Backend Service for more info"
);
}
};
Expand Down Expand Up @@ -70,14 +70,14 @@ const Dashboard = () => {
setLoading(false);
} catch (err) {
toast.error(
"Make sure that backend server is running since its unreachable"
"Check Backend Service for more info"
);
}
};

return (
<>
<Header className="align-center"/>
<Header className="align-center" text="Arka Admin Config Settings"/>
<div className="mb-8">
<TextField
type="text"
Expand Down
4 changes: 2 additions & 2 deletions admin_frontend/src/components/Header.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ const LogoText = styled.span`
color: '#cfcfcf'
`

const Header = () => {
const Header = ({ text }) => {
return (
<div className="flex justify-center w-full items-center mx-auto p-4">
<div className="flex items-center text-cyan-400">
<img src={EtherspotLogo} width={36} height={36} alt={'EtherspotLogo'} />
<LogoText>Arka Admin Config Settings</LogoText>
<LogoText>{text}</LogoText>
</div>
</div>
);
Expand Down
1 change: 1 addition & 0 deletions backend/demo.env
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
NODE_ENV=development
LOG_LEVEL=debug
UNSAFE_MODE=false

API_HOST=127.0.0.1
API_PORT=5050
Expand Down
4 changes: 3 additions & 1 deletion backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,18 @@
"dependencies": {
"@account-abstraction/contracts": "0.6.0",
"@account-abstraction/utils": "0.5.0",
"@aws-sdk/client-secrets-manager": "3.410.0",
"@aws-sdk/client-secrets-manager": "3.450.0",
"@fastify/cors": "8.4.1",
"@sinclair/typebox": "0.31.28",
"ajv": "8.11.2",
"crypto": "^1.0.1",
"dotenv": "16.0.3",
"env-schema": "5.1.1",
"ethers": "5.7.2",
"fastify": "4.24.3",
"fastify-cron": "1.3.1",
"fastify-plugin": "3.0.1",
"getmac": "^6.6.0",
"node-fetch": "3.3.2",
"sqlite": "^5.1.1",
"sqlite3": "^5.1.7-rc.0"
Expand Down
3 changes: 2 additions & 1 deletion backend/src/constants/ErrorMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ export default {
UNSUPPORTED_NETWORK_TOKEN: 'Unsupported network/token',
EMPTY_BODY: 'Empty Body received',
SOMETHING_WENT_WRONG: 'Something went wrong',
INVALID_MODE: 'Invalid mode selected'
INVALID_MODE: 'Invalid mode selected',
DUPLICATE_RECORD: 'Duplicate record found',
}
Loading

0 comments on commit 32fb3a0

Please sign in to comment.