mirror of
https://github.com/Qortal/chrome-extension.git
synced 2025-02-11 17:55:49 +00:00
first commit
This commit is contained in:
commit
0b8db25b24
18
.eslintrc.cjs
Normal file
18
.eslintrc.cjs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
env: { browser: true, es2020: true },
|
||||||
|
extends: [
|
||||||
|
'eslint:recommended',
|
||||||
|
'plugin:@typescript-eslint/recommended',
|
||||||
|
'plugin:react-hooks/recommended',
|
||||||
|
],
|
||||||
|
ignorePatterns: ['dist', '.eslintrc.cjs'],
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
|
plugins: ['react-refresh'],
|
||||||
|
rules: {
|
||||||
|
'react-refresh/only-export-components': [
|
||||||
|
'warn',
|
||||||
|
{ allowConstantExport: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
24
.gitignore
vendored
Normal file
24
.gitignore
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
30
README.md
Normal file
30
README.md
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# React + TypeScript + Vite
|
||||||
|
|
||||||
|
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
||||||
|
|
||||||
|
Currently, two official plugins are available:
|
||||||
|
|
||||||
|
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
|
||||||
|
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
|
||||||
|
|
||||||
|
## Expanding the ESLint configuration
|
||||||
|
|
||||||
|
If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
|
||||||
|
|
||||||
|
- Configure the top-level `parserOptions` property like this:
|
||||||
|
|
||||||
|
```js
|
||||||
|
export default {
|
||||||
|
// other rules...
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 'latest',
|
||||||
|
sourceType: 'module',
|
||||||
|
project: ['./tsconfig.json', './tsconfig.node.json'],
|
||||||
|
tsconfigRootDir: __dirname,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`
|
||||||
|
- Optionally add `plugin:@typescript-eslint/stylistic-type-checked`
|
||||||
|
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list
|
13
index.html
Normal file
13
index.html
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Vite + React + TS</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
4060
package-lock.json
generated
Normal file
4060
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
38
package.json
Normal file
38
package.json
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"name": "ext-one",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "tsc && vite build",
|
||||||
|
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@emotion/react": "^11.11.4",
|
||||||
|
"@emotion/styled": "^11.11.0",
|
||||||
|
"@mui/material": "^5.15.14",
|
||||||
|
"@types/chrome": "^0.0.263",
|
||||||
|
"asmcrypto.js": "2.3.2",
|
||||||
|
"bcryptjs": "2.4.3",
|
||||||
|
"buffer": "6.0.3",
|
||||||
|
"jssha": "3.3.1",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-countdown-circle-timer": "^3.2.1",
|
||||||
|
"react-dom": "^18.2.0",
|
||||||
|
"react-dropzone": "^14.2.3"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/react": "^18.2.64",
|
||||||
|
"@types/react-dom": "^18.2.21",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^7.1.1",
|
||||||
|
"@typescript-eslint/parser": "^7.1.1",
|
||||||
|
"@vitejs/plugin-react": "^4.2.1",
|
||||||
|
"eslint": "^8.57.0",
|
||||||
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
|
"eslint-plugin-react-refresh": "^0.4.5",
|
||||||
|
"typescript": "^5.2.2",
|
||||||
|
"vite": "^5.1.6"
|
||||||
|
}
|
||||||
|
}
|
152
public/content-script.js
Normal file
152
public/content-script.js
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
async function connection(hostname) {
|
||||||
|
const isConnected = await chrome.storage.local.get([hostname]);
|
||||||
|
let connected = false
|
||||||
|
if(isConnected && Object.keys(isConnected).length > 0 && isConnected[hostname]){
|
||||||
|
connected = true
|
||||||
|
}
|
||||||
|
return connected
|
||||||
|
}
|
||||||
|
|
||||||
|
// In your content script
|
||||||
|
document.addEventListener('qortalExtensionRequests', async (event) => {
|
||||||
|
const { type, payload, requestId, timeout } = event.detail; // Capture the requestId
|
||||||
|
console.log({type})
|
||||||
|
if (type === 'REQUEST_USER_INFO') {
|
||||||
|
const hostname = window.location.hostname
|
||||||
|
const res = await connection(hostname)
|
||||||
|
console.log('is connected', res)
|
||||||
|
if(!res){
|
||||||
|
console.log('thou')
|
||||||
|
document.dispatchEvent(new CustomEvent('qortalExtensionResponses', {
|
||||||
|
detail: { type: "USER_INFO", data: {
|
||||||
|
error: "Not authorized"
|
||||||
|
}, requestId }
|
||||||
|
}));
|
||||||
|
return
|
||||||
|
}
|
||||||
|
chrome.runtime.sendMessage({ action: "userInfo" }, (response) => {
|
||||||
|
if (response.error) {
|
||||||
|
document.dispatchEvent(new CustomEvent('qortalExtensionResponses', {
|
||||||
|
detail: { type: "USER_INFO", data: {
|
||||||
|
error: response.error
|
||||||
|
}, requestId }
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
// Include the requestId in the detail when dispatching the response
|
||||||
|
document.dispatchEvent(new CustomEvent('qortalExtensionResponses', {
|
||||||
|
detail: { type: "USER_INFO", data: response, requestId }
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (type === 'REQUEST_IS_INSTALLED') {
|
||||||
|
chrome.runtime.sendMessage({ action: "version" }, (response) => {
|
||||||
|
if (response.error) {
|
||||||
|
console.error("Error:", response.error);
|
||||||
|
} else {
|
||||||
|
// Include the requestId in the detail when dispatching the response
|
||||||
|
document.dispatchEvent(new CustomEvent('qortalExtensionResponses', {
|
||||||
|
detail: { type: "IS_INSTALLED", data: response, requestId }
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (type === 'REQUEST_CONNECTION') {
|
||||||
|
const hostname = window.location.hostname
|
||||||
|
chrome.runtime.sendMessage({ action: "connection", payload: {
|
||||||
|
hostname
|
||||||
|
}, timeout }, (response) => {
|
||||||
|
if (response.error) {
|
||||||
|
console.error("Error:", response.error);
|
||||||
|
} else {
|
||||||
|
// Include the requestId in the detail when dispatching the response
|
||||||
|
document.dispatchEvent(new CustomEvent('qortalExtensionResponses', {
|
||||||
|
detail: { type: "CONNECTION", data: response, requestId }
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (type === 'REQUEST_AUTHENTICATION') {
|
||||||
|
const hostname = window.location.hostname
|
||||||
|
const res = await connection(hostname)
|
||||||
|
if(!res){
|
||||||
|
document.dispatchEvent(new CustomEvent('qortalExtensionResponses', {
|
||||||
|
detail: { type: "USER_INFO", data: {
|
||||||
|
error: "Not authorized"
|
||||||
|
}, requestId }
|
||||||
|
}));
|
||||||
|
return
|
||||||
|
}
|
||||||
|
chrome.runtime.sendMessage({ action: "authentication", payload: {
|
||||||
|
hostname
|
||||||
|
}, timeout }, (response) => {
|
||||||
|
if (response.error) {
|
||||||
|
document.dispatchEvent(new CustomEvent('qortalExtensionResponses', {
|
||||||
|
detail: { type: "AUTHENTICATION", data: {
|
||||||
|
error: response.error
|
||||||
|
}, requestId }
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
// Include the requestId in the detail when dispatching the response
|
||||||
|
document.dispatchEvent(new CustomEvent('qortalExtensionResponses', {
|
||||||
|
detail: { type: "AUTHENTICATION", data: response, requestId }
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (type === 'REQUEST_SEND_QORT') {
|
||||||
|
const hostname = window.location.hostname
|
||||||
|
const res = await connection(hostname)
|
||||||
|
console.log('isconnected', res)
|
||||||
|
if(!res){
|
||||||
|
document.dispatchEvent(new CustomEvent('qortalExtensionResponses', {
|
||||||
|
detail: { type: "USER_INFO", data: {
|
||||||
|
error: "Not authorized"
|
||||||
|
}, requestId }
|
||||||
|
}));
|
||||||
|
return
|
||||||
|
}
|
||||||
|
chrome.runtime.sendMessage({ action: "sendQort", payload: {
|
||||||
|
hostname,
|
||||||
|
amount: payload.amount,
|
||||||
|
description: payload.description,
|
||||||
|
address: payload.address
|
||||||
|
}, timeout }, (response) => {
|
||||||
|
if (response.error) {
|
||||||
|
document.dispatchEvent(new CustomEvent('qortalExtensionResponses', {
|
||||||
|
detail: { type: "SEND_QORT", data: {
|
||||||
|
error: response.error
|
||||||
|
}, requestId }
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
// Include the requestId in the detail when dispatching the response
|
||||||
|
document.dispatchEvent(new CustomEvent('qortalExtensionResponses', {
|
||||||
|
detail: { type: "SEND_QORT", data: response, requestId }
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (type === 'REQUEST_CLOSE_POPUP') {
|
||||||
|
const hostname = window.location.hostname
|
||||||
|
const res = await connection(hostname)
|
||||||
|
if(!res){
|
||||||
|
document.dispatchEvent(new CustomEvent('qortalExtensionResponses', {
|
||||||
|
detail: { type: "USER_INFO", data: {
|
||||||
|
error: "Not authorized"
|
||||||
|
}, requestId }
|
||||||
|
}));
|
||||||
|
return
|
||||||
|
}
|
||||||
|
chrome.runtime.sendMessage({ action: "closePopup" }, (response) => {
|
||||||
|
if (response.error) {
|
||||||
|
|
||||||
|
document.dispatchEvent(new CustomEvent('qortalExtensionResponses', {
|
||||||
|
detail: { type: "CLOSE_POPUP", data: {
|
||||||
|
error: response.error
|
||||||
|
}, requestId }
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
// Include the requestId in the detail when dispatching the response
|
||||||
|
document.dispatchEvent(new CustomEvent('qortalExtensionResponses', {
|
||||||
|
detail: { type: "CLOSE_POPUP", data: true, requestId }
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Handle other request types as needed...
|
||||||
|
});
|
25
public/manifest.json
Normal file
25
public/manifest.json
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"manifest_version": 3,
|
||||||
|
"name": "Minimal Manifest",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"icons": {
|
||||||
|
"32": "qort.png"
|
||||||
|
},
|
||||||
|
"background": {
|
||||||
|
"service_worker": "background.js",
|
||||||
|
"type": "module"
|
||||||
|
},
|
||||||
|
"action": {
|
||||||
|
},
|
||||||
|
"permissions": ["scripting", "storage", "system.display", "activeTab", "tabs", "notifications"
|
||||||
|
],
|
||||||
|
"externally_connectable": {
|
||||||
|
"matches": ["<all_urls>", "*://localhost/*", "*://127.0.0.1/*"]
|
||||||
|
},
|
||||||
|
"content_scripts": [
|
||||||
|
{
|
||||||
|
"matches": ["<all_urls>"],
|
||||||
|
"js": ["content-script.js"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
BIN
public/qort.png
Normal file
BIN
public/qort.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
1
public/vite.svg
Normal file
1
public/vite.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
After Width: | Height: | Size: 1.5 KiB |
42
src/App.css
Normal file
42
src/App.css
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#root {
|
||||||
|
max-width: 1280px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 2rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
height: 6em;
|
||||||
|
padding: 1.5em;
|
||||||
|
will-change: filter;
|
||||||
|
transition: filter 300ms;
|
||||||
|
}
|
||||||
|
.logo:hover {
|
||||||
|
filter: drop-shadow(0 0 2em #646cffaa);
|
||||||
|
}
|
||||||
|
.logo.react:hover {
|
||||||
|
filter: drop-shadow(0 0 2em #61dafbaa);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes logo-spin {
|
||||||
|
from {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: no-preference) {
|
||||||
|
a:nth-of-type(2) .logo {
|
||||||
|
animation: logo-spin infinite 20s linear;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
padding: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.read-the-docs {
|
||||||
|
color: #888;
|
||||||
|
}
|
735
src/App.tsx
Normal file
735
src/App.tsx
Normal file
@ -0,0 +1,735 @@
|
|||||||
|
import { useEffect, useMemo, useState } from "react";
|
||||||
|
import reactLogo from "./assets/react.svg";
|
||||||
|
import viteLogo from "/vite.svg";
|
||||||
|
import "./App.css";
|
||||||
|
import { useDropzone } from "react-dropzone";
|
||||||
|
import { Box, Input, InputLabel, Typography } from "@mui/material";
|
||||||
|
import { decryptStoredWallet } from "./utils/decryptWallet";
|
||||||
|
import { CountdownCircleTimer } from "react-countdown-circle-timer";
|
||||||
|
|
||||||
|
import {
|
||||||
|
createAccount,
|
||||||
|
generateRandomSentence,
|
||||||
|
saveFileToDisk,
|
||||||
|
} from "./utils/generateWallet/generateWallet";
|
||||||
|
import { kdf } from "./deps/kdf";
|
||||||
|
import { generateSaveWalletData } from "./utils/generateWallet/storeWallet";
|
||||||
|
import { crypto, walletVersion } from "./constants/decryptWallet";
|
||||||
|
import PhraseWallet from "./utils/generateWallet/phrase-wallet";
|
||||||
|
|
||||||
|
type extStates =
|
||||||
|
| "not-authenticated"
|
||||||
|
| "authenticated"
|
||||||
|
| "send-qort"
|
||||||
|
| "web-app-request-connection"
|
||||||
|
| "web-app-request-payment"
|
||||||
|
| "web-app-request-authentication"
|
||||||
|
| "download-wallet"
|
||||||
|
| "create-wallet";
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
const [extState, setExtstate] = useState<extStates>("not-authenticated");
|
||||||
|
const [backupjson, setBackupjson] = useState<any>(null);
|
||||||
|
const [rawWallet, setRawWallet] = useState<any>(null);
|
||||||
|
const [decryptedWallet, setdecryptedWallet] = useState<any>(null);
|
||||||
|
const [requestConnection, setRequestConnection] = useState<any>(null);
|
||||||
|
const [requestAuthentication, setRequestAuthentication] = useState<any>(null);
|
||||||
|
const [userInfo, setUserInfo] = useState<any>(null);
|
||||||
|
const [balance, setBalance] = useState<any>(null);
|
||||||
|
const [paymentTo, setPaymentTo] = useState<string>("");
|
||||||
|
const [paymentAmount, setPaymentAmount] = useState<number>(0);
|
||||||
|
const [paymentPassword, setPaymentPassword] = useState<string>("");
|
||||||
|
const [sendPaymentError, setSendPaymentError] = useState<string>("");
|
||||||
|
const [sendPaymentSuccess, setSendPaymentSuccess] = useState<string>("");
|
||||||
|
const [countdown, setCountdown] = useState<null | number>(null);
|
||||||
|
const [walletToBeDownloaded, setWalletToBeDownloaded] = useState<any>(null);
|
||||||
|
const [walletToBeDownloadedPassword, setWalletToBeDownloadedPassword] =
|
||||||
|
useState<string>("");
|
||||||
|
const [walletToBeDownloadedPasswordConfirm, setWalletToBeDownloadedPasswordConfirm] =
|
||||||
|
useState<string>("");
|
||||||
|
const [walletToBeDownloadedError, setWalletToBeDownloadedError] =
|
||||||
|
useState<string>("");
|
||||||
|
|
||||||
|
const address = useMemo(() => {
|
||||||
|
if (!rawWallet?.address0) return "";
|
||||||
|
return rawWallet.address0;
|
||||||
|
}, [rawWallet]);
|
||||||
|
const { getRootProps, getInputProps } = useDropzone({
|
||||||
|
accept: {
|
||||||
|
"application/json": [".json"], // Only accept JSON files
|
||||||
|
},
|
||||||
|
maxFiles: 1,
|
||||||
|
onDrop: async (acceptedFiles) => {
|
||||||
|
const file: any = acceptedFiles[0];
|
||||||
|
const fileContents = await new Promise((resolve, reject) => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
|
||||||
|
reader.onabort = () => reject("File reading was aborted");
|
||||||
|
reader.onerror = () => reject("File reading has failed");
|
||||||
|
reader.onload = () => {
|
||||||
|
// Resolve the promise with the reader result when reading completes
|
||||||
|
resolve(reader.result);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Read the file as text
|
||||||
|
reader.readAsText(file);
|
||||||
|
});
|
||||||
|
console.log({ fileContents });
|
||||||
|
let error: any = null;
|
||||||
|
let pf: any;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (typeof fileContents !== "string") return;
|
||||||
|
pf = JSON.parse(fileContents);
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const requiredFields = [
|
||||||
|
"address0",
|
||||||
|
"salt",
|
||||||
|
"iv",
|
||||||
|
"version",
|
||||||
|
"encryptedSeed",
|
||||||
|
"mac",
|
||||||
|
"kdfThreads",
|
||||||
|
];
|
||||||
|
for (const field of requiredFields) {
|
||||||
|
if (!(field in pf)) throw new Error(field + " not found in JSON");
|
||||||
|
}
|
||||||
|
// setBackupjson(pf)
|
||||||
|
storeWalletInfo(pf);
|
||||||
|
setRawWallet(pf);
|
||||||
|
setExtstate("authenticated");
|
||||||
|
setdecryptedWallet(null);
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// const storeDecryptedWallet = async () => {
|
||||||
|
// const res = await decryptStoredWallet("password", rawWallet);
|
||||||
|
// const wallet2 = new PhraseWallet(res, walletVersion);
|
||||||
|
// setdecryptedWallet(wallet2);
|
||||||
|
// };
|
||||||
|
// const saveWalletFunc = async (password: string) => {
|
||||||
|
// console.log({ decryptedWallet });
|
||||||
|
// let wallet = structuredClone(decryptedWallet);
|
||||||
|
// console.log({ decryptedWallet: decryptedWallet?.generateSaveWalletData });
|
||||||
|
// const qortAddress = decryptedWallet.addresses[0].address;
|
||||||
|
// if (decryptedWallet?.generateSaveWalletData) {
|
||||||
|
// console.log("yes", wallet);
|
||||||
|
// wallet = await decryptedWallet.generateSaveWalletData(
|
||||||
|
// password,
|
||||||
|
// crypto.kdfThreads,
|
||||||
|
// () => {}
|
||||||
|
// );
|
||||||
|
// } else {
|
||||||
|
// console.log("no", wallet);
|
||||||
|
// const res = await decryptStoredWallet(password, wallet);
|
||||||
|
// const wallet2 = new PhraseWallet(res, walletVersion);
|
||||||
|
// console.log({ wallet2 });
|
||||||
|
// wallet = await wallet2.generateSaveWalletData(
|
||||||
|
// password,
|
||||||
|
// crypto.kdfThreads,
|
||||||
|
// () => {}
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// setWalletToBeDownloaded({
|
||||||
|
// wallet,
|
||||||
|
// qortAddress,
|
||||||
|
// });
|
||||||
|
// return {
|
||||||
|
// wallet,
|
||||||
|
// qortAddress,
|
||||||
|
// };
|
||||||
|
// };
|
||||||
|
|
||||||
|
const saveWalletFunc = async (password: string) => {
|
||||||
|
console.log({ decryptedWallet });
|
||||||
|
let wallet = structuredClone(rawWallet);
|
||||||
|
|
||||||
|
console.log("no", wallet);
|
||||||
|
const res = await decryptStoredWallet(password, wallet);
|
||||||
|
const wallet2 = new PhraseWallet(res, walletVersion);
|
||||||
|
console.log({ wallet2 });
|
||||||
|
wallet = await wallet2.generateSaveWalletData(
|
||||||
|
password,
|
||||||
|
crypto.kdfThreads,
|
||||||
|
() => {}
|
||||||
|
);
|
||||||
|
|
||||||
|
setWalletToBeDownloaded({
|
||||||
|
wallet,
|
||||||
|
qortAddress: rawWallet.address0,
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
wallet,
|
||||||
|
qortAddress: rawWallet.address0,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const storeWalletInfo = (wallet: any) => {
|
||||||
|
chrome.runtime.sendMessage(
|
||||||
|
{ action: "storeWalletInfo", wallet },
|
||||||
|
(response) => {
|
||||||
|
if (response) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
chrome.tabs.query(
|
||||||
|
{ active: true, currentWindow: true },
|
||||||
|
function (tabs: any[]) {
|
||||||
|
chrome.tabs.sendMessage(
|
||||||
|
tabs[0].id,
|
||||||
|
{ from: "popup", subject: "anySubject" },
|
||||||
|
function (response) {
|
||||||
|
console.log(response);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
const getWalletInfoFunc = async () => {
|
||||||
|
// const res = await getWalletInfo()
|
||||||
|
// console.log({res})
|
||||||
|
chrome.runtime.sendMessage({ action: "getWalletInfo" }, (response) => {
|
||||||
|
if (response) {
|
||||||
|
console.log("Extension installed: ", response);
|
||||||
|
// setIsExtensionInstalled(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const getValidApiFunc = () => {
|
||||||
|
chrome.runtime.sendMessage({ action: "validApi" }, (response) => {
|
||||||
|
if (response) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const getNameFunc = () => {
|
||||||
|
chrome.runtime.sendMessage({ action: "name" }, (response) => {
|
||||||
|
if (response) {
|
||||||
|
console.log("name", response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const getBalanceFunc = () => {
|
||||||
|
chrome.runtime.sendMessage({ action: "balance" }, (response) => {
|
||||||
|
if (response && !response?.error) {
|
||||||
|
setBalance(response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const sendCoinFunc = () => {
|
||||||
|
setSendPaymentError("");
|
||||||
|
setSendPaymentSuccess("");
|
||||||
|
if (!paymentTo) {
|
||||||
|
setSendPaymentError("Please enter a recipient");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!paymentAmount) {
|
||||||
|
setSendPaymentError("Please enter an amount greater than 0");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!paymentPassword) {
|
||||||
|
setSendPaymentError("Please enter your wallet password");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
chrome.runtime.sendMessage(
|
||||||
|
{
|
||||||
|
action: "sendCoin",
|
||||||
|
payload: {
|
||||||
|
amount: Number(paymentAmount),
|
||||||
|
receiver: paymentTo.trim(),
|
||||||
|
password: paymentPassword,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
(response) => {
|
||||||
|
if (response?.error) {
|
||||||
|
console.log("coin", response);
|
||||||
|
setSendPaymentError(response.error);
|
||||||
|
} else {
|
||||||
|
setSendPaymentSuccess("Payment successfully sent");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log({ rawWallet, decryptedWallet });
|
||||||
|
|
||||||
|
const clearAllStates = () => {
|
||||||
|
setRequestConnection(null);
|
||||||
|
setRequestAuthentication(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const [sendqortState, setSendqortState] = useState<any>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Listen for messages from the background script
|
||||||
|
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||||
|
console.log({ message });
|
||||||
|
// Check if the message is to update the state
|
||||||
|
if (message.action === "UPDATE_STATE_CONFIRM_SEND_QORT") {
|
||||||
|
// Update the component state with the received 'sendqort' state
|
||||||
|
setSendqortState(message.payload);
|
||||||
|
setExtstate("web-app-request-payment");
|
||||||
|
} else if (message.action === "closePopup") {
|
||||||
|
// Update the component state with the received 'sendqort' state
|
||||||
|
window.close();
|
||||||
|
} else if (message.action === "UPDATE_STATE_REQUEST_CONNECTION") {
|
||||||
|
// Update the component state with the received 'sendqort' state
|
||||||
|
setRequestConnection(message.payload);
|
||||||
|
setExtstate("web-app-request-connection");
|
||||||
|
} else if (message.action === "UPDATE_STATE_REQUEST_AUTHENTICATION") {
|
||||||
|
// Update the component state with the received 'sendqort' state
|
||||||
|
setRequestAuthentication(message.payload);
|
||||||
|
setExtstate("web-app-request-authentication");
|
||||||
|
} else if (message.action === "SET_COUNTDOWN") {
|
||||||
|
setCountdown(message.payload);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, []); // Ensure this effect runs only once after component mount
|
||||||
|
|
||||||
|
console.log({ sendqortState });
|
||||||
|
//param = isDecline
|
||||||
|
const confirmPayment = (isDecline: boolean) => {
|
||||||
|
if (isDecline) {
|
||||||
|
chrome.runtime.sendMessage(
|
||||||
|
{
|
||||||
|
action: "sendQortConfirmation",
|
||||||
|
payload: {
|
||||||
|
amount: sendqortState?.amount,
|
||||||
|
receiver: sendqortState?.address,
|
||||||
|
password: paymentPassword,
|
||||||
|
interactionId: sendqortState?.interactionId,
|
||||||
|
isDecline: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
(response) => {
|
||||||
|
if (response) {
|
||||||
|
setSendPaymentSuccess("Payment successfully sent");
|
||||||
|
} else {
|
||||||
|
window.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!paymentPassword) {
|
||||||
|
setSendPaymentError("Please enter your wallet password");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
chrome.runtime.sendMessage(
|
||||||
|
{
|
||||||
|
action: "sendQortConfirmation",
|
||||||
|
payload: {
|
||||||
|
amount: sendqortState.amount,
|
||||||
|
receiver: sendqortState.address,
|
||||||
|
password: paymentPassword,
|
||||||
|
interactionId: sendqortState.interactionId,
|
||||||
|
isDecline: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
(response) => {
|
||||||
|
if (response) {
|
||||||
|
setSendPaymentSuccess("Payment successfully sent");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
const responseToConnectionRequest = (
|
||||||
|
isOkay: boolean,
|
||||||
|
hostname: string,
|
||||||
|
interactionId: string
|
||||||
|
) => {
|
||||||
|
chrome.runtime.sendMessage(
|
||||||
|
{
|
||||||
|
action: "responseToConnectionRequest",
|
||||||
|
payload: { isOkay, hostname, interactionId },
|
||||||
|
},
|
||||||
|
(response) => {
|
||||||
|
if (response === false || response === true) {
|
||||||
|
window.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
try {
|
||||||
|
chrome.runtime.sendMessage({ action: "getWalletInfo" }, (response) => {
|
||||||
|
if (response && response?.walletInfo) {
|
||||||
|
console.log("Extension installed: ", response);
|
||||||
|
setRawWallet(response?.walletInfo);
|
||||||
|
setExtstate("authenticated");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {}
|
||||||
|
}, [address]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!address) return;
|
||||||
|
try {
|
||||||
|
chrome.runtime.sendMessage({ action: "userInfo" }, (response) => {
|
||||||
|
if (response && !response.error) {
|
||||||
|
console.log("Extension installed: ", response);
|
||||||
|
setUserInfo(response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
getBalanceFunc();
|
||||||
|
} catch (error) {}
|
||||||
|
}, [address]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
console.log("exit");
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
console.log({ userInfo });
|
||||||
|
|
||||||
|
const confirmPasswordToDownload = async () => {
|
||||||
|
try {
|
||||||
|
setWalletToBeDownloadedError("");
|
||||||
|
if (!walletToBeDownloadedPassword) {
|
||||||
|
setSendPaymentError("Please enter your password");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const res = await saveWalletFunc(walletToBeDownloadedPassword);
|
||||||
|
} catch (error: any) {
|
||||||
|
setWalletToBeDownloadedError(error?.message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const saveFileToDiskFunc = async () => {
|
||||||
|
try {
|
||||||
|
await saveFileToDisk(
|
||||||
|
walletToBeDownloaded.wallet,
|
||||||
|
walletToBeDownloaded.qortAddress
|
||||||
|
);
|
||||||
|
} catch (error: any) {
|
||||||
|
setWalletToBeDownloadedError(error?.message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const createAccountFunc = async () => {
|
||||||
|
try {
|
||||||
|
if (!walletToBeDownloadedPassword) {
|
||||||
|
setWalletToBeDownloadedError("Please enter a password");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!walletToBeDownloadedPasswordConfirm) {
|
||||||
|
setWalletToBeDownloadedError("Please confirm your password");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (walletToBeDownloadedPasswordConfirm !== walletToBeDownloadedPassword) {
|
||||||
|
setWalletToBeDownloadedError("Password fields do not match!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const res = await createAccount();
|
||||||
|
console.log("new account", res);
|
||||||
|
const wallet = await res.generateSaveWalletData(
|
||||||
|
walletToBeDownloadedPassword,
|
||||||
|
crypto.kdfThreads,
|
||||||
|
() => {}
|
||||||
|
);
|
||||||
|
setRawWallet(wallet);
|
||||||
|
storeWalletInfo(wallet);
|
||||||
|
setWalletToBeDownloaded({
|
||||||
|
wallet,
|
||||||
|
qortAddress: wallet.address0,
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error: any) {
|
||||||
|
setWalletToBeDownloadedError(error?.message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const logoutFunc = ()=> {
|
||||||
|
try {
|
||||||
|
chrome.runtime.sendMessage({ action: "logout" }, (response) => {
|
||||||
|
if (response) {
|
||||||
|
window.close()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* {requestConnection && (
|
||||||
|
<>
|
||||||
|
<p>
|
||||||
|
the application {requestConnection?.hostname} is requesting a
|
||||||
|
connection
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
onClick={() =>
|
||||||
|
responseToConnectionRequest(
|
||||||
|
true,
|
||||||
|
requestConnection?.hostname,
|
||||||
|
requestConnection.interactionId
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
accept
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() =>
|
||||||
|
responseToConnectionRequest(
|
||||||
|
false,
|
||||||
|
requestConnection?.hostname,
|
||||||
|
requestConnection.interactionId
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
decline
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
)} */}
|
||||||
|
|
||||||
|
{/* <button onClick={storeDecryptedWallet}>Decrypt rawWallet</button> */}
|
||||||
|
{/* <button onClick={getWalletInfoFunc}>get saved Wallet info</button> */}
|
||||||
|
{/* <button onClick={getNameFunc}>Get Name</button> */}
|
||||||
|
{/* <button onClick={getBalanceFunc}>Get Balance</button> */}
|
||||||
|
|
||||||
|
{extState === "not-authenticated" && (
|
||||||
|
<>
|
||||||
|
<button onClick={()=> {
|
||||||
|
setExtstate("create-wallet")
|
||||||
|
}}>Create account</button>
|
||||||
|
|
||||||
|
<button {...getRootProps()}>
|
||||||
|
<input {...getInputProps()} />
|
||||||
|
Existing account
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{extState !== "not-authenticated" && (
|
||||||
|
<button onClick={logoutFunc}>logout</button>
|
||||||
|
)}
|
||||||
|
{extState === "authenticated" && (
|
||||||
|
<>
|
||||||
|
<p>{userInfo?.name}</p>
|
||||||
|
<p>balance: {balance}</p>
|
||||||
|
<div>
|
||||||
|
{rawWallet?.address0 && <p>Welcome {rawWallet?.address0}</p>}
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setExtstate("download-wallet");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Download Wallet
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setExtstate("send-qort");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Send Coin
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{extState === "send-qort" && (
|
||||||
|
<>
|
||||||
|
<p>balance: {balance}</p>
|
||||||
|
<Box>
|
||||||
|
<InputLabel htmlFor="standard-adornment-name">To</InputLabel>
|
||||||
|
<Input
|
||||||
|
id="standard-adornment-name"
|
||||||
|
value={paymentTo}
|
||||||
|
onChange={(e) => setPaymentTo(e.target.value)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<InputLabel htmlFor="standard-adornment-amount">Amount</InputLabel>
|
||||||
|
<Input
|
||||||
|
id="standard-adornment-amount"
|
||||||
|
type="number"
|
||||||
|
value={paymentAmount}
|
||||||
|
onChange={(e) => setPaymentAmount(+e.target.value)}
|
||||||
|
/>
|
||||||
|
<InputLabel htmlFor="standard-adornment-password">
|
||||||
|
Confirm wallet password
|
||||||
|
</InputLabel>
|
||||||
|
<Input
|
||||||
|
type="password"
|
||||||
|
id="standard-adornment-password"
|
||||||
|
value={paymentPassword}
|
||||||
|
onChange={(e) => setPaymentPassword(e.target.value)}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Typography color="errror">{sendPaymentError}</Typography>
|
||||||
|
<Typography>{sendPaymentSuccess}</Typography>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
sendCoinFunc();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Send Coin
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{extState === "web-app-request-payment" && (
|
||||||
|
<>
|
||||||
|
<p>{sendqortState?.hostname}</p>
|
||||||
|
<p>{sendqortState?.description}</p>
|
||||||
|
<p>{sendqortState?.address}</p>
|
||||||
|
<InputLabel htmlFor="standard-adornment-password">
|
||||||
|
Confirm wallet password
|
||||||
|
</InputLabel>
|
||||||
|
<Input
|
||||||
|
type="password"
|
||||||
|
id="standard-adornment-password"
|
||||||
|
value={paymentPassword}
|
||||||
|
onChange={(e) => setPaymentPassword(e.target.value)}
|
||||||
|
/>
|
||||||
|
<button onClick={() => confirmPayment(false)}>
|
||||||
|
confirm payment of qort {sendqortState?.amount}
|
||||||
|
</button>
|
||||||
|
<button onClick={() => confirmPayment(true)}>decline</button>
|
||||||
|
<Typography color="errror">{sendPaymentError}</Typography>
|
||||||
|
<Typography>{sendPaymentSuccess}</Typography>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{extState === "web-app-request-connection" && (
|
||||||
|
<>
|
||||||
|
<p>
|
||||||
|
the application {requestConnection?.hostname} is requesting a
|
||||||
|
connection
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
onClick={() =>
|
||||||
|
responseToConnectionRequest(
|
||||||
|
true,
|
||||||
|
requestConnection?.hostname,
|
||||||
|
requestConnection.interactionId
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
accept
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() =>
|
||||||
|
responseToConnectionRequest(
|
||||||
|
false,
|
||||||
|
requestConnection?.hostname,
|
||||||
|
requestConnection.interactionId
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
decline
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{extState === "web-app-request-authentication" && (
|
||||||
|
<>
|
||||||
|
<p>the application {requestConnection?.hostname} you Authenticate</p>
|
||||||
|
<p>Either create a new account or import an existing one</p>
|
||||||
|
<button onClick={()=> {
|
||||||
|
setExtstate("create-wallet")
|
||||||
|
}}>Create account</button>
|
||||||
|
<button {...getRootProps()}>
|
||||||
|
<input {...getInputProps()} />
|
||||||
|
Existing account
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{extState === "download-wallet" && (
|
||||||
|
<>
|
||||||
|
<div>
|
||||||
|
{rawWallet?.address0 && <p>Welcome {rawWallet?.address0}</p>}
|
||||||
|
</div>
|
||||||
|
{!walletToBeDownloaded && (
|
||||||
|
<>
|
||||||
|
<InputLabel htmlFor="standard-adornment-password">
|
||||||
|
Confirm wallet password
|
||||||
|
</InputLabel>
|
||||||
|
<Input
|
||||||
|
type="password"
|
||||||
|
id="standard-adornment-password"
|
||||||
|
value={walletToBeDownloadedPassword}
|
||||||
|
onChange={(e) =>
|
||||||
|
setWalletToBeDownloadedPassword(e.target.value)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<button onClick={confirmPasswordToDownload}>
|
||||||
|
Confirm password
|
||||||
|
</button>
|
||||||
|
<Typography color="errror">{walletToBeDownloadedError}</Typography>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{walletToBeDownloaded && (
|
||||||
|
<>
|
||||||
|
<button onClick={saveFileToDiskFunc}>Download wallet</button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{extState === "create-wallet" && (
|
||||||
|
<>
|
||||||
|
|
||||||
|
{!walletToBeDownloaded && (
|
||||||
|
<>
|
||||||
|
<InputLabel htmlFor="standard-adornment-password">
|
||||||
|
Wallet password
|
||||||
|
</InputLabel>
|
||||||
|
<Input
|
||||||
|
type="password"
|
||||||
|
id="standard-adornment-password"
|
||||||
|
value={walletToBeDownloadedPassword}
|
||||||
|
onChange={(e) =>
|
||||||
|
setWalletToBeDownloadedPassword(e.target.value)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<InputLabel htmlFor="standard-adornment-password">
|
||||||
|
Confirm wallet password
|
||||||
|
</InputLabel>
|
||||||
|
<Input
|
||||||
|
type="password"
|
||||||
|
id="standard-adornment-password"
|
||||||
|
value={walletToBeDownloadedPasswordConfirm}
|
||||||
|
onChange={(e) =>
|
||||||
|
setWalletToBeDownloadedPasswordConfirm(e.target.value)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<button onClick={createAccountFunc}>
|
||||||
|
Create Account
|
||||||
|
</button>
|
||||||
|
<Typography color="errror">{walletToBeDownloadedError}</Typography>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{walletToBeDownloaded && (
|
||||||
|
<>
|
||||||
|
<button onClick={saveFileToDiskFunc}>Download wallet</button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{countdown && (
|
||||||
|
<CountdownCircleTimer
|
||||||
|
isPlaying
|
||||||
|
duration={countdown}
|
||||||
|
colors={["#004777", "#F7B801", "#A30000", "#A30000"]}
|
||||||
|
colorsTime={[7, 5, 2, 0]}
|
||||||
|
onComplete={() => {
|
||||||
|
window.close();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{({ remainingTime }) => remainingTime}
|
||||||
|
</CountdownCircleTimer>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App;
|
1
src/assets/react.svg
Normal file
1
src/assets/react.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>
|
After Width: | Height: | Size: 4.0 KiB |
797
src/background.ts
Normal file
797
src/background.ts
Normal file
@ -0,0 +1,797 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import Base58 from "./deps/Base58";
|
||||||
|
import { createTransaction } from "./transactions/transactions";
|
||||||
|
import { decryptStoredWallet } from "./utils/decryptWallet";
|
||||||
|
import PhraseWallet from "./utils/generateWallet/phrase-wallet";
|
||||||
|
import { validateAddress } from "./utils/validateAddress";
|
||||||
|
|
||||||
|
// chrome.storage.local.clear(function() {
|
||||||
|
// var error = chrome.runtime.lastError;
|
||||||
|
// if (error) {
|
||||||
|
// console.error(error);
|
||||||
|
// } else {
|
||||||
|
// console.log('Local storage cleared');
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
export const walletVersion = 2;
|
||||||
|
// List of your API endpoints
|
||||||
|
const apiEndpoints = [
|
||||||
|
"https://api.qortal.org",
|
||||||
|
"https://webapi.qortal.online",
|
||||||
|
"https://web-api.qortal.online",
|
||||||
|
"https://api.qortal.online",
|
||||||
|
"https://apinode.qortalnodes.live",
|
||||||
|
"https://apinode1.qortalnodes.live",
|
||||||
|
"https://apinode2.qortalnodes.live",
|
||||||
|
"https://apinode3.qortalnodes.live",
|
||||||
|
"https://apinode4.qortalnodes.live",
|
||||||
|
];
|
||||||
|
|
||||||
|
const pendingResponses = new Map();
|
||||||
|
|
||||||
|
// Function to check each API endpoint
|
||||||
|
async function findUsableApi() {
|
||||||
|
for (const endpoint of apiEndpoints) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${endpoint}/admin/status`);
|
||||||
|
if (!response.ok) throw new Error("Failed to fetch");
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
if (data.isSynchronizing === false && data.syncPercent === 100) {
|
||||||
|
console.log(`Usable API found: ${endpoint}`);
|
||||||
|
return endpoint;
|
||||||
|
} else {
|
||||||
|
console.log(`API not ready: ${endpoint}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error checking API ${endpoint}:`, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error("No usable API found");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getNameInfo() {
|
||||||
|
const wallet = await getSaveWallet();
|
||||||
|
const address = wallet.address0;
|
||||||
|
console.log({ address });
|
||||||
|
const validApi = await findUsableApi();
|
||||||
|
const response = await fetch(validApi + "/names/address/" + address);
|
||||||
|
console.log({ response });
|
||||||
|
const nameData = await response.json();
|
||||||
|
console.log({ nameData });
|
||||||
|
if (nameData?.length > 0) {
|
||||||
|
return nameData[0].name;
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function getAddressInfo(address) {
|
||||||
|
const validApi = await findUsableApi();
|
||||||
|
const response = await fetch(validApi + "/addresses/" + address);
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (!response?.ok && data?.error !== 124)
|
||||||
|
throw new Error("Cannot fetch address info");
|
||||||
|
if (data?.error === 124) {
|
||||||
|
return {
|
||||||
|
address,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getSaveWallet() {
|
||||||
|
const res = await chrome.storage.local.get(["walletInfo"]);
|
||||||
|
if (res?.walletInfo) {
|
||||||
|
return res.walletInfo;
|
||||||
|
} else {
|
||||||
|
throw new Error("No wallet saved");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getUserInfo() {
|
||||||
|
console.log("entered");
|
||||||
|
const wallet = await getSaveWallet();
|
||||||
|
const address = wallet.address0;
|
||||||
|
const addressInfo = await getAddressInfo(address);
|
||||||
|
const name = await getNameInfo();
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
...addressInfo,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function connection(hostname) {
|
||||||
|
const isConnected = chrome.storage.local.get([hostname]);
|
||||||
|
return isConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getBalanceInfo() {
|
||||||
|
const wallet = await getSaveWallet();
|
||||||
|
const address = wallet.address0;
|
||||||
|
console.log({ address });
|
||||||
|
const validApi = await findUsableApi();
|
||||||
|
const response = await fetch(validApi + "/addresses/balance/" + address);
|
||||||
|
|
||||||
|
if (!response?.ok) throw new Error("Cannot fetch balance");
|
||||||
|
const data = await response.json();
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
const processTransactionVersion2 = async (body: any) => {
|
||||||
|
const validApi = await findUsableApi();
|
||||||
|
const url = validApi + "/transactions/process?apiVersion=2";
|
||||||
|
return fetch(url, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {},
|
||||||
|
body,
|
||||||
|
}).then(async (response) => {
|
||||||
|
try {
|
||||||
|
const json = await response.clone().json();
|
||||||
|
return json;
|
||||||
|
} catch (e) {
|
||||||
|
return await response.text();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const transaction = async ({ type, params, apiVersion, keyPair }: any) => {
|
||||||
|
|
||||||
|
const tx = createTransaction(type, keyPair, params);
|
||||||
|
console.log({ tx });
|
||||||
|
let res;
|
||||||
|
|
||||||
|
if (apiVersion && apiVersion === 2) {
|
||||||
|
const signedBytes = Base58.encode(tx.signedBytes);
|
||||||
|
res = await processTransactionVersion2(signedBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: res,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const makeTransactionRequest = async (
|
||||||
|
receiver,
|
||||||
|
lastRef,
|
||||||
|
amount,
|
||||||
|
fee,
|
||||||
|
keyPair
|
||||||
|
) => {
|
||||||
|
// let recipientName = await getName(receiver)
|
||||||
|
// let myTxnrequest = await parentEpml.request('transaction', {
|
||||||
|
// type: 2,
|
||||||
|
// nonce: 0,
|
||||||
|
// params: {
|
||||||
|
// recipient: receiver,
|
||||||
|
// recipientName: recipientName,
|
||||||
|
// amount: amount,
|
||||||
|
// lastReference: lastRef,
|
||||||
|
// fee: fee
|
||||||
|
// },
|
||||||
|
// apiVersion: 2
|
||||||
|
// })
|
||||||
|
console.log({ receiver, lastRef, amount, fee });
|
||||||
|
const myTxnrequest = await transaction({
|
||||||
|
nonce: 0,
|
||||||
|
type: 2,
|
||||||
|
params: {
|
||||||
|
recipient: receiver,
|
||||||
|
// recipientName: recipientName,
|
||||||
|
amount: amount,
|
||||||
|
lastReference: lastRef,
|
||||||
|
fee: fee,
|
||||||
|
},
|
||||||
|
apiVersion: 2,
|
||||||
|
keyPair,
|
||||||
|
});
|
||||||
|
return myTxnrequest;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getLastRef = async () => {
|
||||||
|
const wallet = await getSaveWallet();
|
||||||
|
const address = wallet.address0;
|
||||||
|
const validApi = await findUsableApi();
|
||||||
|
const response = await fetch(
|
||||||
|
validApi + "/addresses/lastreference/" + address
|
||||||
|
);
|
||||||
|
if (!response?.ok) throw new Error("Cannot fetch balance");
|
||||||
|
const data = await response.text();
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
const sendQortFee = async () => {
|
||||||
|
const validApi = await findUsableApi();
|
||||||
|
const response = await fetch(
|
||||||
|
validApi + "/transactions/unitfee?txType=PAYMENT"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error("Error when fetching join fee");
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
const qortFee = (Number(data) / 1e8).toFixed(8);
|
||||||
|
return qortFee;
|
||||||
|
};
|
||||||
|
async function getNameOrAddress(receiver) {
|
||||||
|
try {
|
||||||
|
const isAddress = validateAddress(receiver);
|
||||||
|
if (isAddress) {
|
||||||
|
return receiver;
|
||||||
|
}
|
||||||
|
const validApi = await findUsableApi();
|
||||||
|
|
||||||
|
const response = await fetch(validApi + "/names/" + receiver);
|
||||||
|
const data = await response.json();
|
||||||
|
if (data?.owner) return data.owner;
|
||||||
|
if (data?.error) {
|
||||||
|
throw new Error("Name does not exist");
|
||||||
|
}
|
||||||
|
if (!response?.ok) throw new Error("Cannot fetch name");
|
||||||
|
return { error: "cannot validate address or name" };
|
||||||
|
} catch (error) {}
|
||||||
|
}
|
||||||
|
async function sendCoin({ password, amount, receiver }) {
|
||||||
|
try {
|
||||||
|
const confirmReceiver = await getNameOrAddress(receiver);
|
||||||
|
if (confirmReceiver.error)
|
||||||
|
throw new Error("Invalid receiver address or name");
|
||||||
|
const wallet = await getSaveWallet();
|
||||||
|
const response = await decryptStoredWallet(password, wallet);
|
||||||
|
const wallet2 = new PhraseWallet(response, walletVersion);
|
||||||
|
|
||||||
|
const lastRef = await getLastRef();
|
||||||
|
console.log({ lastRef });
|
||||||
|
const fee = await sendQortFee();
|
||||||
|
console.log({ fee });
|
||||||
|
const res = await makeTransactionRequest(
|
||||||
|
confirmReceiver,
|
||||||
|
lastRef,
|
||||||
|
amount,
|
||||||
|
fee,
|
||||||
|
wallet2._addresses[0].keyPair
|
||||||
|
);
|
||||||
|
console.log({ res });
|
||||||
|
return res;
|
||||||
|
} catch (error) {
|
||||||
|
console.log({ error });
|
||||||
|
throw new Error(error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||||
|
console.log({ request });
|
||||||
|
if (request) {
|
||||||
|
switch (request.action) {
|
||||||
|
case "version":
|
||||||
|
// Example: respond with the version
|
||||||
|
sendResponse({ version: "1.0" });
|
||||||
|
break;
|
||||||
|
case "storeWalletInfo":
|
||||||
|
chrome.storage.local.set({ walletInfo: request.wallet }, () => {
|
||||||
|
if (chrome.runtime.lastError) {
|
||||||
|
sendResponse({ error: chrome.runtime.lastError.message });
|
||||||
|
} else {
|
||||||
|
sendResponse({ result: "Data saved successfully" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "getWalletInfo":
|
||||||
|
chrome.storage.local.get(["walletInfo"], (result) => {
|
||||||
|
if (chrome.runtime.lastError) {
|
||||||
|
sendResponse({ error: chrome.runtime.lastError.message });
|
||||||
|
} else if (result.walletInfo) {
|
||||||
|
sendResponse({ walletInfo: result.walletInfo });
|
||||||
|
} else {
|
||||||
|
sendResponse({ error: "No wallet info found" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "validApi":
|
||||||
|
findUsableApi()
|
||||||
|
.then((usableApi) => {
|
||||||
|
console.log("Usable API:", usableApi);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(error.message);
|
||||||
|
});
|
||||||
|
case "name":
|
||||||
|
getNameInfo()
|
||||||
|
.then((name) => {
|
||||||
|
sendResponse(name);
|
||||||
|
console.log("name:", name);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(error.message);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "userInfo":
|
||||||
|
getUserInfo()
|
||||||
|
.then((name) => {
|
||||||
|
sendResponse(name);
|
||||||
|
console.log("name:", name);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
sendResponse({ error: "User not authenticated" });
|
||||||
|
console.error(error.message);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "balance":
|
||||||
|
getBalanceInfo()
|
||||||
|
.then((balance) => {
|
||||||
|
sendResponse(balance);
|
||||||
|
console.log("balance:", balance);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(error.message);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "sendCoin":
|
||||||
|
{
|
||||||
|
const { receiver, password, amount } = request.payload;
|
||||||
|
sendCoin({ receiver, password, amount })
|
||||||
|
.then(() => {
|
||||||
|
sendResponse(true);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
sendResponse({ error: error.message });
|
||||||
|
console.error(error.message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "authentication":
|
||||||
|
{
|
||||||
|
getSaveWallet()
|
||||||
|
.then(() => {
|
||||||
|
sendResponse(true);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
const popupUrl = chrome.runtime.getURL("index.html");
|
||||||
|
console.log({ popupUrl });
|
||||||
|
|
||||||
|
chrome.windows.getAll(
|
||||||
|
{ populate: true, windowTypes: ["popup"] },
|
||||||
|
(windows) => {
|
||||||
|
console.log({ windows });
|
||||||
|
windows.forEach((win) => {
|
||||||
|
win.tabs.forEach((tab) => console.log("url", tab.url)); // Attempt to log URLs directly
|
||||||
|
});
|
||||||
|
// Attempt to find an existing popup window that has a tab with the correct URL
|
||||||
|
const existingPopup = windows.find(
|
||||||
|
(w) =>
|
||||||
|
w.tabs &&
|
||||||
|
w.tabs.some(
|
||||||
|
(tab) => tab.url && tab.url.startsWith(popupUrl)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
console.log({ existingPopup });
|
||||||
|
if (existingPopup) {
|
||||||
|
// If the popup exists but is minimized or not focused, focus it
|
||||||
|
chrome.windows.update(existingPopup.id, {
|
||||||
|
focused: true,
|
||||||
|
state: "normal",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// No existing popup found, create a new one
|
||||||
|
chrome.system.display.getInfo((displays) => {
|
||||||
|
// Assuming the primary display is the first one (adjust logic as needed)
|
||||||
|
const primaryDisplay = displays[0];
|
||||||
|
const screenWidth = primaryDisplay.bounds.width;
|
||||||
|
const windowHeight = 500; // Your window height
|
||||||
|
const windowWidth = 400; // Your window width
|
||||||
|
|
||||||
|
// Calculate left position for the window to appear on the right of the screen
|
||||||
|
const leftPosition = screenWidth - windowWidth;
|
||||||
|
|
||||||
|
// Calculate top position for the window, adjust as desired
|
||||||
|
const topPosition =
|
||||||
|
(primaryDisplay.bounds.height - windowHeight) / 2;
|
||||||
|
|
||||||
|
chrome.windows.create({
|
||||||
|
url: chrome.runtime.getURL("index.html"),
|
||||||
|
type: "popup",
|
||||||
|
width: windowWidth,
|
||||||
|
height: windowHeight,
|
||||||
|
left: leftPosition,
|
||||||
|
top: 0,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const interactionId = Date.now().toString(); // Simple example; consider a better unique ID
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
chrome.runtime.sendMessage({
|
||||||
|
action: "SET_COUNTDOWN",
|
||||||
|
payload: request.timeout ? 0.75 * request.timeout : 60,
|
||||||
|
});
|
||||||
|
chrome.runtime.sendMessage({
|
||||||
|
action: "UPDATE_STATE_REQUEST_AUTHENTICATION",
|
||||||
|
payload: {
|
||||||
|
hostname,
|
||||||
|
interactionId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
// Store sendResponse callback with the interaction ID
|
||||||
|
pendingResponses.set(interactionId, sendResponse);
|
||||||
|
let intervalId = null;
|
||||||
|
const startTime = Date.now();
|
||||||
|
const checkInterval = 3000; // Check every 3 seconds
|
||||||
|
const timeout = request.timeout ? 0.75 * (request.timeout * 1000) : 60000; // Stop after 15 seconds
|
||||||
|
|
||||||
|
const checkFunction = () => {
|
||||||
|
getSaveWallet()
|
||||||
|
.then(() => {
|
||||||
|
clearInterval(intervalId); // Stop checking
|
||||||
|
sendResponse(true); // Perform the success action
|
||||||
|
chrome.runtime.sendMessage({
|
||||||
|
action: "closePopup",
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
// Handle error if needed
|
||||||
|
});
|
||||||
|
|
||||||
|
if (Date.now() - startTime > timeout) {
|
||||||
|
sendResponse({
|
||||||
|
error: "User has not authenticated, try again.",
|
||||||
|
});
|
||||||
|
clearInterval(intervalId); // Stop checking due to timeout
|
||||||
|
console.log("Timeout exceeded");
|
||||||
|
// Handle timeout situation if needed
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
intervalId = setInterval(checkFunction, checkInterval);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "connection":
|
||||||
|
const { hostname } = request.payload;
|
||||||
|
connection(hostname)
|
||||||
|
.then((isConnected) => {
|
||||||
|
console.log({ isConnected });
|
||||||
|
if (Object.keys(isConnected)?.length > 0 && isConnected[hostname]) {
|
||||||
|
sendResponse(true);
|
||||||
|
} else {
|
||||||
|
const popupUrl = chrome.runtime.getURL("index.html");
|
||||||
|
console.log({ popupUrl });
|
||||||
|
|
||||||
|
chrome.windows.getAll(
|
||||||
|
{ populate: true, windowTypes: ["popup"] },
|
||||||
|
(windows) => {
|
||||||
|
console.log({ windows });
|
||||||
|
windows.forEach((win) => {
|
||||||
|
win.tabs.forEach((tab) => console.log("url", tab.url)); // Attempt to log URLs directly
|
||||||
|
});
|
||||||
|
// Attempt to find an existing popup window that has a tab with the correct URL
|
||||||
|
const existingPopup = windows.find(
|
||||||
|
(w) =>
|
||||||
|
w.tabs &&
|
||||||
|
w.tabs.some(
|
||||||
|
(tab) => tab.url && tab.url.startsWith(popupUrl)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
console.log({ existingPopup });
|
||||||
|
if (existingPopup) {
|
||||||
|
// If the popup exists but is minimized or not focused, focus it
|
||||||
|
chrome.windows.update(existingPopup.id, {
|
||||||
|
focused: true,
|
||||||
|
state: "normal",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// No existing popup found, create a new one
|
||||||
|
chrome.system.display.getInfo((displays) => {
|
||||||
|
// Assuming the primary display is the first one (adjust logic as needed)
|
||||||
|
const primaryDisplay = displays[0];
|
||||||
|
const screenWidth = primaryDisplay.bounds.width;
|
||||||
|
const windowHeight = 500; // Your window height
|
||||||
|
const windowWidth = 400; // Your window width
|
||||||
|
|
||||||
|
// Calculate left position for the window to appear on the right of the screen
|
||||||
|
const leftPosition = screenWidth - windowWidth;
|
||||||
|
|
||||||
|
// Calculate top position for the window, adjust as desired
|
||||||
|
const topPosition =
|
||||||
|
(primaryDisplay.bounds.height - windowHeight) / 2;
|
||||||
|
|
||||||
|
chrome.windows.create({
|
||||||
|
url: chrome.runtime.getURL("index.html"),
|
||||||
|
type: "popup",
|
||||||
|
width: windowWidth,
|
||||||
|
height: windowHeight,
|
||||||
|
left: leftPosition,
|
||||||
|
top: 0,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const interactionId = Date.now().toString(); // Simple example; consider a better unique ID
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
chrome.runtime.sendMessage({
|
||||||
|
action: "SET_COUNTDOWN",
|
||||||
|
payload: request.timeout ? 0.9 * request.timeout : 20,
|
||||||
|
});
|
||||||
|
chrome.runtime.sendMessage({
|
||||||
|
action: "UPDATE_STATE_REQUEST_CONNECTION",
|
||||||
|
payload: {
|
||||||
|
hostname,
|
||||||
|
interactionId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
// Store sendResponse callback with the interaction ID
|
||||||
|
pendingResponses.set(interactionId, sendResponse);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(error.message);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "sendQort":
|
||||||
|
{
|
||||||
|
const { amount, hostname, address, description } = request.payload;
|
||||||
|
const popupUrl = chrome.runtime.getURL("index.html");
|
||||||
|
console.log({ popupUrl });
|
||||||
|
|
||||||
|
chrome.windows.getAll(
|
||||||
|
{ populate: true, windowTypes: ["popup"] },
|
||||||
|
(windows) => {
|
||||||
|
console.log({ windows });
|
||||||
|
windows.forEach((win) => {
|
||||||
|
win.tabs.forEach((tab) => console.log("url", tab.url)); // Attempt to log URLs directly
|
||||||
|
});
|
||||||
|
// Attempt to find an existing popup window that has a tab with the correct URL
|
||||||
|
const existingPopup = windows.find(
|
||||||
|
(w) =>
|
||||||
|
w.tabs &&
|
||||||
|
w.tabs.some((tab) => tab.url && tab.url.startsWith(popupUrl))
|
||||||
|
);
|
||||||
|
console.log({ existingPopup });
|
||||||
|
if (existingPopup) {
|
||||||
|
// If the popup exists but is minimized or not focused, focus it
|
||||||
|
chrome.windows.update(existingPopup.id, {
|
||||||
|
focused: true,
|
||||||
|
state: "normal",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// No existing popup found, create a new one
|
||||||
|
chrome.system.display.getInfo((displays) => {
|
||||||
|
// Assuming the primary display is the first one (adjust logic as needed)
|
||||||
|
const primaryDisplay = displays[0];
|
||||||
|
const screenWidth = primaryDisplay.bounds.width;
|
||||||
|
const windowHeight = 500; // Your window height
|
||||||
|
const windowWidth = 400; // Your window width
|
||||||
|
|
||||||
|
// Calculate left position for the window to appear on the right of the screen
|
||||||
|
const leftPosition = screenWidth - windowWidth;
|
||||||
|
|
||||||
|
// Calculate top position for the window, adjust as desired
|
||||||
|
const topPosition =
|
||||||
|
(primaryDisplay.bounds.height - windowHeight) / 2;
|
||||||
|
|
||||||
|
chrome.windows.create({
|
||||||
|
url: chrome.runtime.getURL("index.html"),
|
||||||
|
type: "popup",
|
||||||
|
width: windowWidth,
|
||||||
|
height: windowHeight,
|
||||||
|
left: leftPosition,
|
||||||
|
top: 0,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const interactionId = Date.now().toString(); // Simple example; consider a better unique ID
|
||||||
|
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
chrome.runtime.sendMessage({
|
||||||
|
action: "SET_COUNTDOWN",
|
||||||
|
payload: request.timeout ? 0.9 * request.timeout : 20,
|
||||||
|
});
|
||||||
|
chrome.runtime.sendMessage({
|
||||||
|
action: "UPDATE_STATE_CONFIRM_SEND_QORT",
|
||||||
|
payload: {
|
||||||
|
amount,
|
||||||
|
address,
|
||||||
|
hostname,
|
||||||
|
description,
|
||||||
|
interactionId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
// Store sendResponse callback with the interaction ID
|
||||||
|
pendingResponses.set(interactionId, sendResponse);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "responseToConnectionRequest":
|
||||||
|
{
|
||||||
|
const { hostname, isOkay } = request.payload;
|
||||||
|
console.log({ hostname, isOkay });
|
||||||
|
const interactionId3 = request.payload.interactionId;
|
||||||
|
if (!isOkay) {
|
||||||
|
const originalSendResponse = pendingResponses.get(interactionId3);
|
||||||
|
if (originalSendResponse) {
|
||||||
|
originalSendResponse(false);
|
||||||
|
sendResponse(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const originalSendResponse = pendingResponses.get(interactionId3);
|
||||||
|
if (originalSendResponse) {
|
||||||
|
// Example of setting domain permission
|
||||||
|
chrome.storage.local.set({ [hostname]: true });
|
||||||
|
|
||||||
|
originalSendResponse(true);
|
||||||
|
sendResponse(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "sendQortConfirmation":
|
||||||
|
const { password, amount, receiver, isDecline } = request.payload;
|
||||||
|
const interactionId2 = request.payload.interactionId;
|
||||||
|
|
||||||
|
// Retrieve the stored sendResponse callback
|
||||||
|
const originalSendResponse = pendingResponses.get(interactionId2);
|
||||||
|
|
||||||
|
if (originalSendResponse) {
|
||||||
|
if (isDecline) {
|
||||||
|
originalSendResponse({ error: "User has declined" });
|
||||||
|
sendResponse(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sendCoin({ password, amount, receiver })
|
||||||
|
.then((res) => {
|
||||||
|
sendResponse(true);
|
||||||
|
// Use the sendResponse callback to respond to the original message
|
||||||
|
originalSendResponse(res);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(error.message);
|
||||||
|
sendResponse(false);
|
||||||
|
originalSendResponse({ error: error.message });
|
||||||
|
});
|
||||||
|
|
||||||
|
// Remove the callback from the Map as it's no longer needed
|
||||||
|
pendingResponses.delete(interactionId2);
|
||||||
|
}
|
||||||
|
console.log({ password, amount, receiver });
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "logout" : {
|
||||||
|
chrome.storage.local.remove('walletInfo', () => {
|
||||||
|
if (chrome.runtime.lastError) {
|
||||||
|
// Handle error
|
||||||
|
console.error(chrome.runtime.lastError.message);
|
||||||
|
} else {
|
||||||
|
// Data removed successfully
|
||||||
|
sendResponse(true)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
// chrome.runtime.onMessageExternal.addListener(function (
|
||||||
|
// request,
|
||||||
|
// sender,
|
||||||
|
// sendResponse
|
||||||
|
// ) {
|
||||||
|
// console.log({ request });
|
||||||
|
// if (request) {
|
||||||
|
// switch (request.action) {
|
||||||
|
// case "version":
|
||||||
|
// // Example: respond with the version
|
||||||
|
// sendResponse({ version: "1.0" });
|
||||||
|
// break;
|
||||||
|
// case "storeWalletInfo":
|
||||||
|
// chrome.storage.local.set({ walletInfo: request.wallet }, () => {
|
||||||
|
// if (chrome.runtime.lastError) {
|
||||||
|
// sendResponse({ error: chrome.runtime.lastError.message });
|
||||||
|
// } else {
|
||||||
|
// sendResponse({ result: "Data saved successfully" });
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// break;
|
||||||
|
// case "getWalletInfo":
|
||||||
|
// chrome.storage.local.get(["walletInfo"], (result) => {
|
||||||
|
// if (chrome.runtime.lastError) {
|
||||||
|
// sendResponse({ error: chrome.runtime.lastError.message });
|
||||||
|
// } else if (result.walletInfo) {
|
||||||
|
// sendResponse({ walletInfo: result.walletInfo });
|
||||||
|
// } else {
|
||||||
|
// sendResponse({ error: "No wallet info found" });
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return true; // This is crucial for asynchronous sendResponse
|
||||||
|
// });
|
||||||
|
|
||||||
|
chrome.action.onClicked.addListener((tab) => {
|
||||||
|
const popupUrl = chrome.runtime.getURL("index.html");
|
||||||
|
chrome.windows.getAll(
|
||||||
|
{ populate: true, windowTypes: ["popup"] },
|
||||||
|
(windows) => {
|
||||||
|
console.log({ windows });
|
||||||
|
windows.forEach((win) => {
|
||||||
|
win.tabs.forEach((tab) => console.log("url", tab.url)); // Attempt to log URLs directly
|
||||||
|
});
|
||||||
|
// Attempt to find an existing popup window that has a tab with the correct URL
|
||||||
|
const existingPopup = windows.find(
|
||||||
|
(w) =>
|
||||||
|
w.tabs &&
|
||||||
|
w.tabs.some((tab) => tab.url && tab.url.startsWith(popupUrl))
|
||||||
|
);
|
||||||
|
console.log({ existingPopup });
|
||||||
|
if (existingPopup) {
|
||||||
|
// If the popup exists but is minimized or not focused, focus it
|
||||||
|
chrome.windows.update(existingPopup.id, {
|
||||||
|
focused: true,
|
||||||
|
state: "normal",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// No existing popup found, create a new one
|
||||||
|
chrome.system.display.getInfo((displays) => {
|
||||||
|
// Assuming the primary display is the first one (adjust logic as needed)
|
||||||
|
const primaryDisplay = displays[0];
|
||||||
|
const screenWidth = primaryDisplay.bounds.width;
|
||||||
|
const windowHeight = 500; // Your window height
|
||||||
|
const windowWidth = 400; // Your window width
|
||||||
|
|
||||||
|
// Calculate left position for the window to appear on the right of the screen
|
||||||
|
const leftPosition = screenWidth - windowWidth;
|
||||||
|
|
||||||
|
// Calculate top position for the window, adjust as desired
|
||||||
|
const topPosition = (primaryDisplay.bounds.height - windowHeight) / 2;
|
||||||
|
|
||||||
|
chrome.windows.create({
|
||||||
|
url: chrome.runtime.getURL("index.html"),
|
||||||
|
type: "popup",
|
||||||
|
width: windowWidth,
|
||||||
|
height: windowHeight,
|
||||||
|
left: leftPosition,
|
||||||
|
top: 0,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const interactionId = Date.now().toString(); // Simple example; consider a better unique ID
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
chrome.runtime.sendMessage({
|
||||||
|
action: "UPDATE_STATE_REQUEST_CONNECTION",
|
||||||
|
payload: {
|
||||||
|
hostname,
|
||||||
|
interactionId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
// Store sendResponse callback with the interaction ID
|
||||||
|
pendingResponses.set(interactionId, sendResponse);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
175
src/constants/constants.ts
Normal file
175
src/constants/constants.ts
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
|
||||||
|
|
||||||
|
// Qortal TX types
|
||||||
|
const TX_TYPES = {
|
||||||
|
1: "Genesis",
|
||||||
|
2: "Payment",
|
||||||
|
3: "Name registration",
|
||||||
|
4: "Name update",
|
||||||
|
5: "Sell name",
|
||||||
|
6: "Cancel sell name",
|
||||||
|
7: "Buy name",
|
||||||
|
8: "Create poll",
|
||||||
|
9: "Vote in poll",
|
||||||
|
10: "Arbitrary",
|
||||||
|
11: "Issue asset",
|
||||||
|
12: "Transfer asset",
|
||||||
|
13: "Create asset order",
|
||||||
|
14: "Cancel asset order",
|
||||||
|
15: "Multi-payment transaction",
|
||||||
|
16: "Deploy AT",
|
||||||
|
17: "Message",
|
||||||
|
18: "Chat",
|
||||||
|
19: "Publicize",
|
||||||
|
20: "Airdrop",
|
||||||
|
21: "AT",
|
||||||
|
22: "Create group",
|
||||||
|
23: "Update group",
|
||||||
|
24: "Add group admin",
|
||||||
|
25: "Remove group admin",
|
||||||
|
26: "Group ban",
|
||||||
|
27: "Cancel group ban",
|
||||||
|
28: "Group kick",
|
||||||
|
29: "Group invite",
|
||||||
|
30: "Cancel group invite",
|
||||||
|
31: "Join group",
|
||||||
|
32: "Leave group",
|
||||||
|
33: "Group approval",
|
||||||
|
34: "Set group",
|
||||||
|
35: "Update asset",
|
||||||
|
36: "Account flags",
|
||||||
|
37: "Enable forging",
|
||||||
|
38: "Reward share",
|
||||||
|
39: "Account level",
|
||||||
|
40: "Transfer privs",
|
||||||
|
41: "Presence"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Qortal error codes
|
||||||
|
const ERROR_CODES = {
|
||||||
|
1: "Valid OK",
|
||||||
|
2: "Invalid address",
|
||||||
|
3: "Negative amount",
|
||||||
|
4: "Nagative fee",
|
||||||
|
5: "No balance",
|
||||||
|
6: "Invalid reference",
|
||||||
|
7: "Invalid time length",
|
||||||
|
8: "Invalid value length",
|
||||||
|
9: "Name already registered",
|
||||||
|
10: "Name does not exist",
|
||||||
|
11: "Invalid name owner",
|
||||||
|
12: "Name already for sale",
|
||||||
|
13: "Name not for sale",
|
||||||
|
14: "Name buyer already owner",
|
||||||
|
15: "Invalid amount",
|
||||||
|
16: "Invalid seller",
|
||||||
|
17: "Name not lowercase",
|
||||||
|
18: "Invalid description length",
|
||||||
|
19: "Invalid options length",
|
||||||
|
20: "Invalid option length",
|
||||||
|
21: "Duplicate option",
|
||||||
|
22: "Poll already created",
|
||||||
|
23: "Poll already has votes",
|
||||||
|
24: "Poll does not exist",
|
||||||
|
25: "Option does not exist",
|
||||||
|
26: "Already voted for that option",
|
||||||
|
27: "Invalid data length",
|
||||||
|
28: "Invalid quantity",
|
||||||
|
29: "Asset does not exist",
|
||||||
|
30: "Invalid return",
|
||||||
|
31: "Have equals want",
|
||||||
|
32: "Order does not exist",
|
||||||
|
33: "Invalid order creator",
|
||||||
|
34: "Invalid payments length",
|
||||||
|
35: "Negative price",
|
||||||
|
36: "Invalid creation bytes",
|
||||||
|
37: "Invalid tags length",
|
||||||
|
38: "Invalid type length",
|
||||||
|
39: "Invalid AT transaction",
|
||||||
|
40: "Insufficient fee",
|
||||||
|
41: "Asset does not match AT",
|
||||||
|
|
||||||
|
43: "Asset already exists",
|
||||||
|
44: "Missing creator",
|
||||||
|
45: "Timestamp too old",
|
||||||
|
46: "Timestamp too new",
|
||||||
|
47: "Too many unconfirmed",
|
||||||
|
48: "Group already exists",
|
||||||
|
49: "Group does not exist",
|
||||||
|
50: "Invalid group owner",
|
||||||
|
51: "Already group memeber",
|
||||||
|
52: "Group owner can not leave",
|
||||||
|
53: "Not group member",
|
||||||
|
54: "Already group admin",
|
||||||
|
55: "Not group admin",
|
||||||
|
56: "Invalid lifetime",
|
||||||
|
57: "Invite unknown",
|
||||||
|
58: "Ban exists",
|
||||||
|
59: "Ban unknown",
|
||||||
|
60: "Banned from group",
|
||||||
|
61: "Join request",
|
||||||
|
62: "Invalid group approval threshold",
|
||||||
|
63: "Group ID mismatch",
|
||||||
|
64: "Invalid group ID",
|
||||||
|
65: "Transaction unknown",
|
||||||
|
66: "Transaction already confirmed",
|
||||||
|
67: "Invalid TX group",
|
||||||
|
68: "TX group ID mismatch",
|
||||||
|
69: "Multiple names forbidden",
|
||||||
|
70: "Invalid asset owner",
|
||||||
|
71: "AT is finished",
|
||||||
|
72: "No flag permission",
|
||||||
|
73: "Not minting accout",
|
||||||
|
|
||||||
|
77: "Invalid rewardshare percent",
|
||||||
|
78: "Public key unknown",
|
||||||
|
79: "Invalid public key",
|
||||||
|
80: "AT unknown",
|
||||||
|
81: "AT already exists",
|
||||||
|
82: "Group approval not required",
|
||||||
|
83: "Group approval decided",
|
||||||
|
84: "Maximum reward shares",
|
||||||
|
85: "Transaction already exists",
|
||||||
|
86: "No blockchain lock",
|
||||||
|
87: "Order already closed",
|
||||||
|
88: "Clock not synced",
|
||||||
|
89: "Asset not spendable",
|
||||||
|
90: "Account can not reward share",
|
||||||
|
91: "Self share exists",
|
||||||
|
92: "Account already exists",
|
||||||
|
93: "Invalid group block delay",
|
||||||
|
94: "Incorrect nonce",
|
||||||
|
95: "Ivalid timestamp signature",
|
||||||
|
96: "Address blocked",
|
||||||
|
97: "Name Blocked",
|
||||||
|
98: "Group approval required",
|
||||||
|
99: "Account not transferable",
|
||||||
|
|
||||||
|
999: "Ivalid but ok",
|
||||||
|
1000: "Not yet released."
|
||||||
|
}
|
||||||
|
|
||||||
|
// Qortal 8 decimals
|
||||||
|
const QORT_DECIMALS = 1e8
|
||||||
|
|
||||||
|
// Q for Qortal
|
||||||
|
const ADDRESS_VERSION = 58
|
||||||
|
|
||||||
|
// Proxy for api calls
|
||||||
|
const PROXY_URL = "/proxy/"
|
||||||
|
|
||||||
|
// Chat reference timestamp
|
||||||
|
const CHAT_REFERENCE_FEATURE_TRIGGER_TIMESTAMP = 1674316800000
|
||||||
|
|
||||||
|
// Dynamic fee timestamp
|
||||||
|
const DYNAMIC_FEE_TIMESTAMP = 1692118800000
|
||||||
|
|
||||||
|
|
||||||
|
// Used as a salt for all Qora addresses. Salts used for storing your private keys in local storage will be randomly generated
|
||||||
|
const STATIC_SALT = new Uint8Array([54, 190, 201, 206, 65, 29, 123, 129, 147, 231, 180, 166, 171, 45, 95, 165, 78, 200, 208, 194, 44, 207, 221, 146, 45, 238, 68, 68, 69, 102, 62, 6])
|
||||||
|
const BCRYPT_ROUNDS = 10 // Remember that the total work spent on key derivation is BCRYPT_ROUNDS * KDF_THREADS
|
||||||
|
const BCRYPT_VERSION = "2a"
|
||||||
|
const STATIC_BCRYPT_SALT = `$${BCRYPT_VERSION}$${BCRYPT_ROUNDS}$IxVE941tXVUD4cW0TNVm.O`
|
||||||
|
const KDF_THREADS = 16
|
||||||
|
|
||||||
|
export { TX_TYPES, ERROR_CODES, QORT_DECIMALS, PROXY_URL, STATIC_SALT, ADDRESS_VERSION, KDF_THREADS, STATIC_BCRYPT_SALT, CHAT_REFERENCE_FEATURE_TRIGGER_TIMESTAMP, DYNAMIC_FEE_TIMESTAMP }
|
11
src/constants/decryptWallet.ts
Normal file
11
src/constants/decryptWallet.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
export const crypto = {
|
||||||
|
kdfThreads: 16,
|
||||||
|
staticSalt: '4ghkVQExoneGqZqHTMMhhFfxXsVg2A75QeS1HCM5KAih', // Base58 encoded
|
||||||
|
bcryptRounds: 11, // Note it's kinda bcryptRounds * log.2.16, cause it runs on all 16 threads
|
||||||
|
bcryptVersion: '2a',
|
||||||
|
get staticBcryptSalt () {
|
||||||
|
return `$${this.bcryptVersion}$${this.bcryptRounds}$IxVE941tXVUD4cW0TNVm.O`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const walletVersion = 2
|
5
src/constants/general.ts
Normal file
5
src/constants/general.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
// Qortal 8 decimals
|
||||||
|
export const QORT_DECIMALS = 1e8
|
||||||
|
|
||||||
|
// Q for Qortal
|
||||||
|
export const ADDRESS_VERSION = 58
|
864
src/deps/AltcoinHDWallet.ts
Normal file
864
src/deps/AltcoinHDWallet.ts
Normal file
@ -0,0 +1,864 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
;
|
||||||
|
import Base58 from '../deps/Base58.js'
|
||||||
|
import {Sha256, Sha512} from 'asmcrypto.js'
|
||||||
|
import jsSHA from 'jssha'
|
||||||
|
import RIPEMD160 from '../deps/ripemd160.js'
|
||||||
|
import utils from '../utils/utils'
|
||||||
|
import {BigInteger, EllipticCurve} from './ecbn'
|
||||||
|
import {Buffer} from 'buffer'
|
||||||
|
|
||||||
|
export default class AltcoinHDWallet {
|
||||||
|
|
||||||
|
constructor(addressParams) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Seed - 32 bytes
|
||||||
|
*/
|
||||||
|
|
||||||
|
this.seed = new Uint8Array(32)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Version Bytes - 4 byte
|
||||||
|
*/
|
||||||
|
|
||||||
|
this.versionBytes = addressParams
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Depth - 1 byte
|
||||||
|
*/
|
||||||
|
|
||||||
|
this.depth = 0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parent Fingerprint - 4 bytes
|
||||||
|
*/
|
||||||
|
|
||||||
|
this.parentFingerprint = '0x00000000' // master key
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Child Index - 4 bytes
|
||||||
|
*/
|
||||||
|
|
||||||
|
this.childIndex = '0x00000000' // master key
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chain Code - 32 bytes
|
||||||
|
*/
|
||||||
|
|
||||||
|
this.chainCode = new Uint8Array(32)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Key Data - 33 bytes
|
||||||
|
*/
|
||||||
|
|
||||||
|
this.keyData = new Uint8Array(33)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Seed Hash - 64 bytes
|
||||||
|
*/
|
||||||
|
|
||||||
|
this.seedHash = new Uint8Array(64)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private Key - 32 bytes
|
||||||
|
*/
|
||||||
|
|
||||||
|
this.privateKey = new Uint8Array(32)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Public Key - 33 bytes (compressed)
|
||||||
|
*/
|
||||||
|
|
||||||
|
this.publicKey = new Uint8Array(33)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Public Key Hash160 (used to derive the parent fingerprint for derived)
|
||||||
|
*/
|
||||||
|
|
||||||
|
this.publicKeyHash = new Uint8Array(20)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Master Private Key (Base58 encoded)
|
||||||
|
*/
|
||||||
|
|
||||||
|
this.masterPrivateKey = ''
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Master Public Key (Base58 encoded)
|
||||||
|
*/
|
||||||
|
|
||||||
|
this.masterPublicKey = ''
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Testnet Master Private Key (Base58 encoded) - THIS IS TESTNET
|
||||||
|
*/
|
||||||
|
|
||||||
|
this._tMasterPrivateKey = ''
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Testnet Master Public Key (Base58 encoded) - THIS IS TESTNET
|
||||||
|
*/
|
||||||
|
|
||||||
|
this._tmasterPublicKey = ''
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Child Keys Derivation from the Parent Keys
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Child Private Key - 32 bytes
|
||||||
|
*/
|
||||||
|
|
||||||
|
this.childPrivateKey = new Uint8Array(32)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Child Chain Code - 32 bytes
|
||||||
|
*/
|
||||||
|
|
||||||
|
this.childChainCode = new Uint8Array(32)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Child Public Key - 33 bytes (compressed)
|
||||||
|
*/
|
||||||
|
|
||||||
|
this.childPublicKey = new Uint8Array(33)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Child Public Key Hash160 (used to derive the parent fingerprint for derived)
|
||||||
|
*/
|
||||||
|
|
||||||
|
this.childPublicKeyHash = new Uint8Array(20)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extended Private Child Key - Base58 encoded
|
||||||
|
*/
|
||||||
|
|
||||||
|
this.xPrivateChildKey = ''
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extended Public Child Key - Base58 encoded
|
||||||
|
*/
|
||||||
|
|
||||||
|
this.xPublicChildKey = ''
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Grand Child Keys Derivation from the Child Keys
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Grand Child Private Key - 32 bytes
|
||||||
|
*/
|
||||||
|
|
||||||
|
this.grandChildPrivateKey = new Uint8Array(32)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Grand Child Chain Code - 32 bytes
|
||||||
|
*/
|
||||||
|
|
||||||
|
this.grandChildChainCode = new Uint8Array(32)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Grand Child Public Key - 33 bytes (compressed)
|
||||||
|
*/
|
||||||
|
|
||||||
|
this.grandChildPublicKey = new Uint8Array(33)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Grand Public Key Hash160 (used to derive the parent fingerprint for derived)
|
||||||
|
*/
|
||||||
|
|
||||||
|
this.grandChildPublicKeyHash = new Uint8Array(20)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extended Private Grand Child Key - Base58 encoded
|
||||||
|
*/
|
||||||
|
|
||||||
|
this.xPrivateGrandChildKey = ''
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extended Public Grand Child Key - Base58 encoded
|
||||||
|
*/
|
||||||
|
|
||||||
|
this.xPublicGrandChildKey = ''
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Litecoin Legacy Address - Derived from the Grand Child Public Key Hash
|
||||||
|
*/
|
||||||
|
|
||||||
|
this.litecoinLegacyAddress = ''
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TESTNET Litecoin Legacy Address (Derived from the Grand Child Public Key Hash) - THIS IS TESTNET
|
||||||
|
*/
|
||||||
|
|
||||||
|
this._tlitecoinLegacyAddress = ''
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wallet - Wallet Object (keys...)
|
||||||
|
*/
|
||||||
|
|
||||||
|
this.wallet = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
setSeed(seed) {
|
||||||
|
this.seed = seed
|
||||||
|
}
|
||||||
|
|
||||||
|
createWallet(seed, isBIP44, indicator = null) {
|
||||||
|
|
||||||
|
// Set Seeed
|
||||||
|
this.setSeed(seed)
|
||||||
|
|
||||||
|
// Generate Seed Hash
|
||||||
|
this.generateSeedHash(this.seed, isBIP44, indicator)
|
||||||
|
|
||||||
|
// Generate Private Key
|
||||||
|
this.generatePrivateKey(this.seedHash)
|
||||||
|
|
||||||
|
// Generate Chain Code
|
||||||
|
this.generateChainCode(this.seedHash)
|
||||||
|
|
||||||
|
// Generate Public Key from Private Key
|
||||||
|
this.generatePublicKey(this.privateKey)
|
||||||
|
|
||||||
|
// Generate Mainnet Master Private Key
|
||||||
|
this.generateMainnetMasterPrivateKey()
|
||||||
|
|
||||||
|
// Generate Mainnet Master Public Key
|
||||||
|
this.generateMainnetMasterPublicKey()
|
||||||
|
|
||||||
|
// Generate Testnet Master Private Key
|
||||||
|
this.generateTestnetMasterPrivateKey()
|
||||||
|
|
||||||
|
// Generate Testnet Master Public Key
|
||||||
|
this.generateTestnetMasterPublicKey()
|
||||||
|
|
||||||
|
// Generate Child and Grand Child Keys
|
||||||
|
this.generateDerivedChildKeys()
|
||||||
|
|
||||||
|
// Return Wallet Object Specification
|
||||||
|
return this.returnWallet()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
generateSeedHash(seed, isBIP44, indicator = null) {
|
||||||
|
let buffer
|
||||||
|
|
||||||
|
if (isBIP44) {
|
||||||
|
buffer = utils.appendBuffer(seed.reverse(), utils.int32ToBytes(indicator))
|
||||||
|
} else {
|
||||||
|
if(indicator !== null) {
|
||||||
|
const indicatorString = utils.stringtoUTF8Array(indicator)
|
||||||
|
buffer = utils.appendBuffer(seed.reverse(), indicatorString)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
buffer = seed.reverse()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const _reverseSeedHash = new Sha256().process(buffer).finish().result
|
||||||
|
this.seedHash = new Sha512().process(utils.appendBuffer(seed, _reverseSeedHash)).finish().result
|
||||||
|
}
|
||||||
|
|
||||||
|
generatePrivateKey(seedHash) {
|
||||||
|
const SECP256K1_CURVE_ORDER = new BigInteger("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141")
|
||||||
|
|
||||||
|
const privateKeyHash = seedHash.slice(0, 32)
|
||||||
|
|
||||||
|
this.seed58 = Base58.encode(privateKeyHash)
|
||||||
|
|
||||||
|
const _privateKeyHash = [...privateKeyHash]
|
||||||
|
let privateKeyBigInt = BigInteger.fromByteArrayUnsigned(_privateKeyHash)
|
||||||
|
|
||||||
|
const privateKey = (privateKeyBigInt.mod(SECP256K1_CURVE_ORDER.subtract(BigInteger.ONE))).add(BigInteger.ONE)
|
||||||
|
this.privateKey = privateKey.toByteArrayUnsigned()
|
||||||
|
}
|
||||||
|
|
||||||
|
generateChainCode(seedHash) {
|
||||||
|
this.chainCode = new Sha256().process(seedHash.slice(32, 64)).finish().result
|
||||||
|
}
|
||||||
|
|
||||||
|
generatePublicKey(privateKey) {
|
||||||
|
const _privateKey = [...privateKey]
|
||||||
|
const privateKeyBigInt = BigInteger.fromByteArrayUnsigned(_privateKey)
|
||||||
|
|
||||||
|
const epCurve = EllipticCurve.getSECCurveByName("secp256k1")
|
||||||
|
const curvePoints = epCurve.getG().multiply(privateKeyBigInt)
|
||||||
|
|
||||||
|
|
||||||
|
const x = curvePoints.getX().toBigInteger()
|
||||||
|
const y = curvePoints.getY().toBigInteger()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deriving Uncompressed Public Key (65 bytes)
|
||||||
|
*
|
||||||
|
* const publicKeyBytes = EllipticCurve.integerToBytes(x, 32)
|
||||||
|
* this.publicKey = publicKeyBytes.concat(EllipticCurve.integerToBytes(y, 32))
|
||||||
|
* this.publicKey.unshift(0x04) // append point indicator
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Compressed Public Key (33 bytes)
|
||||||
|
this.publicKey = EllipticCurve.integerToBytes(x, 32)
|
||||||
|
|
||||||
|
|
||||||
|
if (y.isEven()) {
|
||||||
|
this.publicKey.unshift(0x02) // append point indicator
|
||||||
|
} else {
|
||||||
|
this.publicKey.unshift(0x03) // append point indicator
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublicKey Hash
|
||||||
|
const publicKeySHA256 = new Sha256().process(new Uint8Array(this.publicKey)).finish().result
|
||||||
|
const _publicKeyHash = new RIPEMD160().update(Buffer.from(publicKeySHA256)).digest('hex')
|
||||||
|
this.publicKeyHash = _publicKeyHash
|
||||||
|
}
|
||||||
|
|
||||||
|
generateMainnetMasterPrivateKey() {
|
||||||
|
// Serialization Variable
|
||||||
|
const s = []
|
||||||
|
|
||||||
|
// Append Version Byte
|
||||||
|
s.push(...(utils.int32ToBytes(this.versionBytes.mainnet.private)))
|
||||||
|
|
||||||
|
// Append Depth
|
||||||
|
s.push(this.depth)
|
||||||
|
|
||||||
|
// Append Parent Fingerprint
|
||||||
|
s.push(...(utils.int32ToBytes(this.parentFingerprint)))
|
||||||
|
|
||||||
|
// Append Child Number
|
||||||
|
s.push(...(utils.int32ToBytes(this.childIndex)))
|
||||||
|
|
||||||
|
// Append Chain Code
|
||||||
|
s.push(...this.chainCode)
|
||||||
|
|
||||||
|
// Append 1 byte '0x00' (to make the key data 33 bytes, DO THIS ONLY FOR PRIVATE KEYS )
|
||||||
|
s.push(0)
|
||||||
|
|
||||||
|
//if the private key length is less than 32 let's add leading zeros
|
||||||
|
if(this.privateKey.length<32){
|
||||||
|
for(let i=this.privateKey.length;i<32;i++){
|
||||||
|
s.push(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append Private Key
|
||||||
|
s.push(...this.privateKey)
|
||||||
|
|
||||||
|
// Generate CheckSum
|
||||||
|
const _s = new Uint8Array(s)
|
||||||
|
const _checkSum = new Sha256().process(new Sha256().process(_s).finish().result).finish().result
|
||||||
|
const checkSum = _checkSum.slice(0, 4)
|
||||||
|
|
||||||
|
// Append CheckSum
|
||||||
|
s.push(...checkSum) // And this brings us to the end of the serialization...
|
||||||
|
|
||||||
|
// Save to Private Key as Base58 encoded
|
||||||
|
this.masterPrivateKey = Base58.encode(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
generateMainnetMasterPublicKey() {
|
||||||
|
// Serialization Variable
|
||||||
|
const s = []
|
||||||
|
|
||||||
|
// Append Version Byte
|
||||||
|
s.push(...(utils.int32ToBytes(this.versionBytes.mainnet.public)))
|
||||||
|
|
||||||
|
// Append Depth
|
||||||
|
s.push(this.depth)
|
||||||
|
|
||||||
|
// Append Parent Fingerprint
|
||||||
|
s.push(...(utils.int32ToBytes(this.parentFingerprint)))
|
||||||
|
|
||||||
|
// Append Child Number
|
||||||
|
s.push(...(utils.int32ToBytes(this.childIndex)))
|
||||||
|
|
||||||
|
// Append Chain Code
|
||||||
|
s.push(...this.chainCode)
|
||||||
|
|
||||||
|
// Append Public Key
|
||||||
|
s.push(...this.publicKey)
|
||||||
|
|
||||||
|
// Generate CheckSum
|
||||||
|
const _s = new Uint8Array(s)
|
||||||
|
const _checkSum = new Sha256().process(new Sha256().process(_s).finish().result).finish().result
|
||||||
|
const checkSum = _checkSum.slice(0, 4)
|
||||||
|
|
||||||
|
// Append CheckSum
|
||||||
|
s.push(...checkSum) // And this brings us to the end of the serialization...
|
||||||
|
|
||||||
|
// Save to Public Key as Base58 encoded
|
||||||
|
this.masterPublicKey = Base58.encode(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
generateTestnetMasterPrivateKey() {
|
||||||
|
|
||||||
|
// To be Used ONLY in Testnet...
|
||||||
|
|
||||||
|
// Serialization Variable
|
||||||
|
const s = []
|
||||||
|
|
||||||
|
// Append Version Byte
|
||||||
|
s.push(...(utils.int32ToBytes(this.versionBytes.testnet.private)))
|
||||||
|
|
||||||
|
// Append Depth
|
||||||
|
s.push(this.depth)
|
||||||
|
|
||||||
|
// Append Parent Fingerprint
|
||||||
|
s.push(...(utils.int32ToBytes(this.parentFingerprint)))
|
||||||
|
|
||||||
|
// Append Child Number
|
||||||
|
s.push(...(utils.int32ToBytes(this.childIndex)))
|
||||||
|
|
||||||
|
// Append Chain Code
|
||||||
|
s.push(...this.chainCode)
|
||||||
|
|
||||||
|
// Append 1 byte '0x00' (to make the key data 33 bytes, DO THIS ONLY FOR PRIVATE KEYS )
|
||||||
|
s.push(0)
|
||||||
|
|
||||||
|
// Append Private Key
|
||||||
|
s.push(...this.privateKey)
|
||||||
|
|
||||||
|
// Generate CheckSum
|
||||||
|
const _s = new Uint8Array(s)
|
||||||
|
const _checkSum = new Sha256().process(new Sha256().process(_s).finish().result).finish().result
|
||||||
|
const checkSum = _checkSum.slice(0, 4)
|
||||||
|
|
||||||
|
// Append CheckSum
|
||||||
|
s.push(...checkSum) // And this brings us to the end of the serialization...
|
||||||
|
|
||||||
|
// Save to Private Key as Base58 encoded
|
||||||
|
this._tMasterPrivateKey = Base58.encode(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
generateTestnetMasterPublicKey() {
|
||||||
|
|
||||||
|
// To be Used ONLY in Testnet...
|
||||||
|
|
||||||
|
// Serialization Variable
|
||||||
|
const s = []
|
||||||
|
|
||||||
|
// Append Version Byte
|
||||||
|
s.push(...(utils.int32ToBytes(this.versionBytes.testnet.public)))
|
||||||
|
|
||||||
|
// Append Depth
|
||||||
|
s.push(this.depth)
|
||||||
|
|
||||||
|
// Append Parent Fingerprint
|
||||||
|
s.push(...(utils.int32ToBytes(this.parentFingerprint)))
|
||||||
|
|
||||||
|
// Append Child Number
|
||||||
|
s.push(...(utils.int32ToBytes(this.childIndex)))
|
||||||
|
|
||||||
|
// Append Chain Code
|
||||||
|
s.push(...this.chainCode)
|
||||||
|
|
||||||
|
// Append Private Key
|
||||||
|
s.push(...this.publicKey)
|
||||||
|
|
||||||
|
// Generate CheckSum
|
||||||
|
const _s = new Uint8Array(s)
|
||||||
|
const _checkSum = new Sha256().process(new Sha256().process(_s).finish().result).finish().result
|
||||||
|
const checkSum = _checkSum.slice(0, 4)
|
||||||
|
|
||||||
|
// Append CheckSum
|
||||||
|
s.push(...checkSum) // And this brings us to the end of the serialization...
|
||||||
|
|
||||||
|
// Save to Private Key as Base58 encoded
|
||||||
|
this._tmasterPublicKey = Base58.encode(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
generateDerivedChildKeys() {
|
||||||
|
|
||||||
|
// SPEC INFO: https://en.bitcoin.it/wiki/BIP_0032#Child_key_derivation_.28CKD.29_functions
|
||||||
|
// NOTE: will not be using some of derivations func as the value is known. (So I'd rather shove in the values and rewrite out the derivations later ?)
|
||||||
|
|
||||||
|
// NOTE: I "re-wrote" and "reduplicate" the code for child and grandChild keys derivations inorder to get the child and grandchild from the child
|
||||||
|
// TODO: Make this more better in the future
|
||||||
|
|
||||||
|
const path = 'm/0/0'
|
||||||
|
// let p = path.split('/')
|
||||||
|
|
||||||
|
// Get Public kEY
|
||||||
|
const derivePublicChildKey = () => {
|
||||||
|
|
||||||
|
const _privateKey = [...this.childPrivateKey]
|
||||||
|
const privateKeyBigInt = BigInteger.fromByteArrayUnsigned(_privateKey)
|
||||||
|
|
||||||
|
const epCurve = EllipticCurve.getSECCurveByName("secp256k1")
|
||||||
|
const curvePoints = epCurve.getG().multiply(privateKeyBigInt)
|
||||||
|
|
||||||
|
const x = curvePoints.getX().toBigInteger()
|
||||||
|
const y = curvePoints.getY().toBigInteger()
|
||||||
|
|
||||||
|
// Compressed Public Key (33 bytes)
|
||||||
|
this.childPublicKey = EllipticCurve.integerToBytes(x, 32)
|
||||||
|
|
||||||
|
|
||||||
|
if (y.isEven()) {
|
||||||
|
|
||||||
|
this.childPublicKey.unshift(0x02) // append point indicator
|
||||||
|
} else {
|
||||||
|
|
||||||
|
this.childPublicKey.unshift(0x03) // append point indicator
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublicKey Hash
|
||||||
|
const childPublicKeySHA256 = new Sha256().process(new Uint8Array(this.childPublicKey)).finish().result
|
||||||
|
const _childPublicKeyHash = new RIPEMD160().update(Buffer.from(childPublicKeySHA256)).digest('hex')
|
||||||
|
this.childPublicKeyHash = _childPublicKeyHash
|
||||||
|
|
||||||
|
|
||||||
|
// Call deriveExtendedPublicChildKey // WIll be hardcoding the values...
|
||||||
|
deriveExtendedPublicChildKey(1, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
const derivePrivateChildKey = (cI) => {
|
||||||
|
|
||||||
|
let ib = []
|
||||||
|
ib.push((cI >> 24) & 0xff)
|
||||||
|
ib.push((cI >> 16) & 0xff)
|
||||||
|
ib.push((cI >> 8) & 0xff)
|
||||||
|
ib.push(cI & 0xff)
|
||||||
|
|
||||||
|
const s = [...this.publicKey].concat(ib)
|
||||||
|
|
||||||
|
const _hmacSha512 = new jsSHA("SHA-512", "UINT8ARRAY", { numRounds: 1, hmacKey: { value: this.chainCode, format: "UINT8ARRAY" } })
|
||||||
|
_hmacSha512.update(new Uint8Array(s))
|
||||||
|
|
||||||
|
|
||||||
|
const IL = BigInteger.fromByteArrayUnsigned([..._hmacSha512.getHMAC('UINT8ARRAY').slice(0, 32)])
|
||||||
|
this.childChainCode = _hmacSha512.getHMAC('UINT8ARRAY').slice(32, 64) // IR according to the SPEC
|
||||||
|
|
||||||
|
|
||||||
|
// SECP256k1 init
|
||||||
|
const epCurve = EllipticCurve.getSECCurveByName("secp256k1")
|
||||||
|
|
||||||
|
|
||||||
|
const ki = IL.add(BigInteger.fromByteArrayUnsigned(this.privateKey)).mod(epCurve.getN()) // parse256(IL) + kpar (mod n) ==> ki
|
||||||
|
this.childPrivateKey = ki.toByteArrayUnsigned()
|
||||||
|
|
||||||
|
// Call deriveExtendedPrivateChildKey
|
||||||
|
deriveExtendedPrivateChildKey(1, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const deriveExtendedPrivateChildKey = (i, childIndex) => {
|
||||||
|
|
||||||
|
// Serialization Variable
|
||||||
|
const s = []
|
||||||
|
|
||||||
|
// Append Version Byte
|
||||||
|
s.push(...(utils.int32ToBytes(this.versionBytes.mainnet.private)))
|
||||||
|
|
||||||
|
// Append Depth (using the index as depth)
|
||||||
|
i = parseInt(i)
|
||||||
|
s.push(i)
|
||||||
|
|
||||||
|
// Append Parent Fingerprint
|
||||||
|
s.push(...(this.publicKeyHash.slice(0, 4)))
|
||||||
|
|
||||||
|
// Append Child Index
|
||||||
|
s.push(childIndex >>> 24)
|
||||||
|
s.push((childIndex >>> 16) & 0xff)
|
||||||
|
s.push((childIndex >>> 8) & 0xff)
|
||||||
|
s.push(childIndex & 0xff)
|
||||||
|
|
||||||
|
// Append Chain Code
|
||||||
|
s.push(...this.childChainCode)
|
||||||
|
|
||||||
|
// Append 1 byte '0x00' (to make the key data 33 bytes, DO THIS ONLY FOR PRIVATE KEYS )
|
||||||
|
s.push(0)
|
||||||
|
|
||||||
|
// Append Private Key
|
||||||
|
s.push(...this.childPrivateKey)
|
||||||
|
|
||||||
|
// Generate CheckSum
|
||||||
|
const _s = new Uint8Array(s)
|
||||||
|
const _checkSum = new Sha256().process(new Sha256().process(_s).finish().result).finish().result
|
||||||
|
const checkSum = _checkSum.slice(0, 4)
|
||||||
|
|
||||||
|
// Append CheckSum
|
||||||
|
s.push(...checkSum) // And this brings us to the end of the serialization...
|
||||||
|
|
||||||
|
// Save to Private Key as Base58 encoded
|
||||||
|
this.xPrivateChildKey = Base58.encode(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
const deriveExtendedPublicChildKey = (i, childIndex) => {
|
||||||
|
|
||||||
|
// Serialization Variable
|
||||||
|
const s = []
|
||||||
|
|
||||||
|
// Append Version Byte
|
||||||
|
s.push(...(utils.int32ToBytes(this.versionBytes.mainnet.public)))
|
||||||
|
|
||||||
|
// Append Depth
|
||||||
|
i = parseInt(i)
|
||||||
|
s.push(i)
|
||||||
|
|
||||||
|
// Append Parent Fingerprint
|
||||||
|
s.push(...(this.publicKeyHash.slice(0, 4)))
|
||||||
|
|
||||||
|
// Append Child Index
|
||||||
|
s.push(childIndex >>> 24)
|
||||||
|
s.push((childIndex >>> 16) & 0xff)
|
||||||
|
s.push((childIndex >>> 8) & 0xff)
|
||||||
|
s.push(childIndex & 0xff)
|
||||||
|
|
||||||
|
// Append Chain Code
|
||||||
|
s.push(...this.childChainCode)
|
||||||
|
|
||||||
|
// Append Public Key
|
||||||
|
s.push(...this.childPublicKey)
|
||||||
|
|
||||||
|
// Generate CheckSum
|
||||||
|
const _s = new Uint8Array(s)
|
||||||
|
const _checkSum = new Sha256().process(new Sha256().process(_s).finish().result).finish().result
|
||||||
|
const checkSum = _checkSum.slice(0, 4)
|
||||||
|
|
||||||
|
// Append CheckSum
|
||||||
|
s.push(...checkSum) // And this brings us to the end of the serialization...
|
||||||
|
|
||||||
|
|
||||||
|
// Save to Public Key as Base58 encoded
|
||||||
|
this.xPublicChildKey = Base58.encode(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GRAND CHILD KEYS
|
||||||
|
*
|
||||||
|
* NOTE: I know this is not the best way to generate this (even though it works the way it ought)
|
||||||
|
* Things to rewrite will be and not limited to deriving this through a for loop, removing hard code values, etc...
|
||||||
|
*/
|
||||||
|
|
||||||
|
const derivePublicGrandChildKey = () => {
|
||||||
|
|
||||||
|
const _privateKey = [...this.grandChildPrivateKey]
|
||||||
|
const privateKeyBigInt = BigInteger.fromByteArrayUnsigned(_privateKey)
|
||||||
|
|
||||||
|
|
||||||
|
const epCurve = EllipticCurve.getSECCurveByName("secp256k1")
|
||||||
|
const curvePoints = epCurve.getG().multiply(privateKeyBigInt)
|
||||||
|
|
||||||
|
const x = curvePoints.getX().toBigInteger()
|
||||||
|
const y = curvePoints.getY().toBigInteger()
|
||||||
|
|
||||||
|
// Compressed Public Key (33 bytes)
|
||||||
|
this.grandChildPublicKey = EllipticCurve.integerToBytes(x, 32)
|
||||||
|
|
||||||
|
|
||||||
|
if (y.isEven()) {
|
||||||
|
this.grandChildPublicKey.unshift(0x02) // append point indicator
|
||||||
|
} else {
|
||||||
|
this.grandChildPublicKey.unshift(0x03) // append point indicator
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// PublicKey Hash
|
||||||
|
const grandChildPublicKeySHA256 = new Sha256().process(new Uint8Array(this.grandChildPublicKey)).finish().result
|
||||||
|
const _grandChildPublicKeyHash = new RIPEMD160().update(Buffer.from(grandChildPublicKeySHA256)).digest('hex')
|
||||||
|
this.grandChildPublicKeyHash = _grandChildPublicKeyHash
|
||||||
|
|
||||||
|
|
||||||
|
// Call deriveExtendedPublicChildKey // WIll be hardcoding the values...
|
||||||
|
deriveExtendedPublicGrandChildKey(2, 0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Derive Litecoin Legacy Address
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Append Address Prefix
|
||||||
|
let prefix = [this.versionBytes.mainnet.prefix]
|
||||||
|
if (2 == this.versionBytes.mainnet.prefix.length) {
|
||||||
|
prefix = [this.versionBytes.mainnet.prefix[0]]
|
||||||
|
prefix.push([this.versionBytes.mainnet.prefix[1]])
|
||||||
|
}
|
||||||
|
|
||||||
|
const k = prefix.concat(...this.grandChildPublicKeyHash)
|
||||||
|
|
||||||
|
// Derive Checksum
|
||||||
|
const _addressCheckSum = new Sha256().process(new Sha256().process(new Uint8Array(k)).finish().result).finish().result
|
||||||
|
const addressCheckSum = _addressCheckSum.slice(0, 4)
|
||||||
|
|
||||||
|
// Append CheckSum
|
||||||
|
const _litecoinLegacyAddress = k.concat(...addressCheckSum)
|
||||||
|
|
||||||
|
// Convert to Base58
|
||||||
|
this.litecoinLegacyAddress = Base58.encode(_litecoinLegacyAddress)
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Derive TESTNET Litecoin Legacy Address
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Append Version Byte
|
||||||
|
const tK = [this.versionBytes.testnet.prefix].concat(...this.grandChildPublicKeyHash)
|
||||||
|
|
||||||
|
// Derive Checksum
|
||||||
|
const _tAddressCheckSum = new Sha256().process(new Sha256().process(new Uint8Array(tK)).finish().result).finish().result
|
||||||
|
const tAddressCheckSum = _tAddressCheckSum.slice(0, 4)
|
||||||
|
|
||||||
|
// Append CheckSum
|
||||||
|
const _tlitecoinLegacyAddress = tK.concat(...tAddressCheckSum)
|
||||||
|
|
||||||
|
// Convert to Base58
|
||||||
|
this._tlitecoinLegacyAddress = Base58.encode(_tlitecoinLegacyAddress)
|
||||||
|
}
|
||||||
|
|
||||||
|
const derivePrivateGrandChildKey = (cI, i) => {
|
||||||
|
|
||||||
|
let ib = []
|
||||||
|
ib.push((cI >> 24) & 0xff)
|
||||||
|
ib.push((cI >> 16) & 0xff)
|
||||||
|
ib.push((cI >> 8) & 0xff)
|
||||||
|
ib.push(cI & 0xff)
|
||||||
|
|
||||||
|
const s = [...this.childPublicKey].concat(ib)
|
||||||
|
|
||||||
|
|
||||||
|
const _hmacSha512 = new jsSHA("SHA-512", "UINT8ARRAY", { numRounds: 1, hmacKey: { value: this.childChainCode, format: "UINT8ARRAY" } })
|
||||||
|
_hmacSha512.update(new Uint8Array(s))
|
||||||
|
|
||||||
|
|
||||||
|
const IL = BigInteger.fromByteArrayUnsigned([..._hmacSha512.getHMAC('UINT8ARRAY').slice(0, 32)])
|
||||||
|
this.grandChildChainCode = _hmacSha512.getHMAC('UINT8ARRAY').slice(32, 64) // IR according to the SPEC
|
||||||
|
|
||||||
|
// SECP256k1 init
|
||||||
|
const epCurve = EllipticCurve.getSECCurveByName("secp256k1")
|
||||||
|
|
||||||
|
|
||||||
|
const ki = IL.add(BigInteger.fromByteArrayUnsigned(this.childPrivateKey)).mod(epCurve.getN()) // parse256(IL) + kpar (mod n) ==> ki
|
||||||
|
this.grandChildPrivateKey = ki.toByteArrayUnsigned()
|
||||||
|
|
||||||
|
|
||||||
|
// Call deriveExtendedPrivateChildKey
|
||||||
|
deriveExtendedPrivateGrandChildKey(2, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const deriveExtendedPrivateGrandChildKey = (i, childIndex) => {
|
||||||
|
|
||||||
|
// Serialization Variable
|
||||||
|
const s = []
|
||||||
|
|
||||||
|
// Append Version Byte
|
||||||
|
s.push(...(utils.int32ToBytes(this.versionBytes.mainnet.private)))
|
||||||
|
|
||||||
|
// Append Depth (using the index as depth)
|
||||||
|
i = parseInt(i)
|
||||||
|
s.push(i)
|
||||||
|
|
||||||
|
// Append Parent Fingerprint
|
||||||
|
s.push(...(this.childPublicKeyHash.slice(0, 4)))
|
||||||
|
|
||||||
|
// Append Child Index
|
||||||
|
s.push(childIndex >>> 24)
|
||||||
|
s.push((childIndex >>> 16) & 0xff)
|
||||||
|
s.push((childIndex >>> 8) & 0xff)
|
||||||
|
s.push(childIndex & 0xff)
|
||||||
|
|
||||||
|
// Append Chain Code
|
||||||
|
s.push(...this.grandChildChainCode)
|
||||||
|
|
||||||
|
// Append 1 byte '0x00' (to make the key data 33 bytes, DO THIS ONLY FOR PRIVATE KEYS )
|
||||||
|
s.push(0)
|
||||||
|
|
||||||
|
// Append Private Key
|
||||||
|
s.push(...this.grandChildPrivateKey)
|
||||||
|
|
||||||
|
// Generate CheckSum
|
||||||
|
const _s = new Uint8Array(s)
|
||||||
|
const _checkSum = new Sha256().process(new Sha256().process(_s).finish().result).finish().result
|
||||||
|
const checkSum = _checkSum.slice(0, 4)
|
||||||
|
|
||||||
|
// Append CheckSum
|
||||||
|
s.push(...checkSum) // And this brings us to the end of the serialization...
|
||||||
|
|
||||||
|
// Save to Private Key as Base58 encoded
|
||||||
|
this.xPrivateGrandChildKey = Base58.encode(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
const deriveExtendedPublicGrandChildKey = (i, childIndex) => {
|
||||||
|
|
||||||
|
// Serialization Variable
|
||||||
|
const s = []
|
||||||
|
|
||||||
|
// Append Version Byte
|
||||||
|
s.push(...(utils.int32ToBytes(this.versionBytes.mainnet.public)))
|
||||||
|
|
||||||
|
// Append Depth
|
||||||
|
i = parseInt(i)
|
||||||
|
s.push(i)
|
||||||
|
|
||||||
|
// Append Parent Fingerprint
|
||||||
|
s.push(...(this.childPublicKeyHash.slice(0, 4)))
|
||||||
|
|
||||||
|
// Append Child Index
|
||||||
|
s.push(childIndex >>> 24)
|
||||||
|
s.push((childIndex >>> 16) & 0xff)
|
||||||
|
s.push((childIndex >>> 8) & 0xff)
|
||||||
|
s.push(childIndex & 0xff)
|
||||||
|
|
||||||
|
// Append Chain Code
|
||||||
|
s.push(...this.grandChildChainCode)
|
||||||
|
|
||||||
|
// Append Public Key
|
||||||
|
s.push(...this.grandChildPublicKey)
|
||||||
|
|
||||||
|
// Generate CheckSum
|
||||||
|
const _s = new Uint8Array(s)
|
||||||
|
const _checkSum = new Sha256().process(new Sha256().process(_s).finish().result).finish().result
|
||||||
|
const checkSum = _checkSum.slice(0, 4)
|
||||||
|
|
||||||
|
// Append CheckSum
|
||||||
|
s.push(...checkSum) // And this brings us to the end of the serialization...
|
||||||
|
|
||||||
|
// Save to Public Key as Base58 encoded
|
||||||
|
this.xPublicGrandChildKey = Base58.encode(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Hard Code value..
|
||||||
|
let childIndex = 0
|
||||||
|
|
||||||
|
// Call derivePrivateChildKey //Hard code value
|
||||||
|
derivePrivateChildKey(childIndex)
|
||||||
|
|
||||||
|
// Call derivePublicChildKey
|
||||||
|
derivePublicChildKey()
|
||||||
|
|
||||||
|
|
||||||
|
// Call derivePrivateGrandChildKey // Hard Code value...
|
||||||
|
derivePrivateGrandChildKey(0, 2)
|
||||||
|
|
||||||
|
// Call derivePublicGrandChildKey
|
||||||
|
derivePublicGrandChildKey()
|
||||||
|
}
|
||||||
|
|
||||||
|
returnWallet() {
|
||||||
|
|
||||||
|
// Will be limiting the exported Wallet Object to just the Master keys and Legacy Addresses
|
||||||
|
|
||||||
|
const wallet = {
|
||||||
|
derivedMasterPrivateKey: this.masterPrivateKey,
|
||||||
|
derivedMasterPublicKey: this.masterPublicKey,
|
||||||
|
_tDerivedMasterPrivateKey: this._tMasterPrivateKey,
|
||||||
|
_tDerivedmasterPublicKey: this._tmasterPublicKey,
|
||||||
|
seed58: this.seed58,
|
||||||
|
// derivedPrivateChildKey: this.xPrivateChildKey,
|
||||||
|
// derivedPublicChildKey: this.xPublicChildKey,
|
||||||
|
// derivedPrivateGrandChildKey: this.xPrivateGrandChildKey,
|
||||||
|
// derivedPublicGrandChildKey: this.xPublicGrandChildKey,
|
||||||
|
address: this.litecoinLegacyAddress,
|
||||||
|
_taddress: this._tlitecoinLegacyAddress
|
||||||
|
}
|
||||||
|
|
||||||
|
this.wallet = wallet
|
||||||
|
return wallet
|
||||||
|
}
|
||||||
|
}
|
103
src/deps/Base58.ts
Normal file
103
src/deps/Base58.ts
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
|
||||||
|
const Base58 = {};
|
||||||
|
|
||||||
|
const ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
||||||
|
|
||||||
|
const ALPHABET_MAP = {};
|
||||||
|
|
||||||
|
let i = 0;
|
||||||
|
|
||||||
|
while (i < ALPHABET.length) {
|
||||||
|
ALPHABET_MAP[ALPHABET.charAt(i)] = i;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Base58.encode = function(buffer) {
|
||||||
|
buffer = new Uint8Array(buffer);
|
||||||
|
var carry, digits, j;
|
||||||
|
if (buffer.length === 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
i = void 0;
|
||||||
|
j = void 0;
|
||||||
|
digits = [0];
|
||||||
|
i = 0;
|
||||||
|
while (i < buffer.length) {
|
||||||
|
j = 0;
|
||||||
|
while (j < digits.length) {
|
||||||
|
digits[j] <<= 8;
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
digits[0] += buffer[i];
|
||||||
|
carry = 0;
|
||||||
|
j = 0;
|
||||||
|
while (j < digits.length) {
|
||||||
|
digits[j] += carry;
|
||||||
|
carry = (digits[j] / 58) | 0;
|
||||||
|
digits[j] %= 58;
|
||||||
|
++j;
|
||||||
|
}
|
||||||
|
while (carry) {
|
||||||
|
digits.push(carry % 58);
|
||||||
|
carry = (carry / 58) | 0;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
i = 0;
|
||||||
|
while (buffer[i] === 0 && i < buffer.length - 1) {
|
||||||
|
digits.push(0);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return digits.reverse().map(function(digit) {
|
||||||
|
return ALPHABET[digit];
|
||||||
|
}).join("");
|
||||||
|
};
|
||||||
|
|
||||||
|
Base58.decode = function(string) {
|
||||||
|
var bytes, c, carry, j;
|
||||||
|
if (string.length === 0) {
|
||||||
|
return new (typeof Uint8Array !== "undefined" && Uint8Array !== null ? Uint8Array : Buffer)(0);
|
||||||
|
}
|
||||||
|
i = void 0;
|
||||||
|
j = void 0;
|
||||||
|
bytes = [0];
|
||||||
|
i = 0;
|
||||||
|
while (i < string.length) {
|
||||||
|
c = string[i];
|
||||||
|
if (!(c in ALPHABET_MAP)) {
|
||||||
|
throw "Base58.decode received unacceptable input. Character '" + c + "' is not in the Base58 alphabet.";
|
||||||
|
}
|
||||||
|
j = 0;
|
||||||
|
while (j < bytes.length) {
|
||||||
|
bytes[j] *= 58;
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
bytes[0] += ALPHABET_MAP[c];
|
||||||
|
carry = 0;
|
||||||
|
j = 0;
|
||||||
|
while (j < bytes.length) {
|
||||||
|
bytes[j] += carry;
|
||||||
|
carry = bytes[j] >> 8;
|
||||||
|
bytes[j] &= 0xff;
|
||||||
|
++j;
|
||||||
|
}
|
||||||
|
while (carry) {
|
||||||
|
bytes.push(carry & 0xff);
|
||||||
|
carry >>= 8;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
i = 0;
|
||||||
|
while (string[i] === "1" && i < string.length - 1) {
|
||||||
|
bytes.push(0);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return new (typeof Uint8Array !== "undefined" && Uint8Array !== null ? Uint8Array : Buffer)(bytes.reverse());
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// == Changed for ES6 modules == //
|
||||||
|
//}).call(this);
|
||||||
|
|
||||||
|
export default Base58;
|
182
src/deps/broken-ripemd160.ts
Normal file
182
src/deps/broken-ripemd160.ts
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
|
||||||
|
// @ts-nocheck
|
||||||
|
// "Generated from Java with JSweet 1.0.0 - http://www.jsweet.org";
|
||||||
|
// BAD IMPLEMENTATION. BROKEN, BUT MUST KEEP CAUSE OF NETWORK
|
||||||
|
//const RIPEMD160 = (function () {
|
||||||
|
// == Convert to ES6 module for export == //
|
||||||
|
const RIPEMD160 = (function () {
|
||||||
|
function RIPEMD160() {
|
||||||
|
this.MDbuf = [];
|
||||||
|
this.MDbuf[0] = 1732584193;
|
||||||
|
this.MDbuf[1] = -271733879;
|
||||||
|
this.MDbuf[2] = -1732584194;
|
||||||
|
this.MDbuf[3] = 271733878;
|
||||||
|
this.MDbuf[4] = -1009589776;
|
||||||
|
this.working = new Int32Array(16);
|
||||||
|
|
||||||
|
this.working_ptr = 0;
|
||||||
|
this.msglen = 0;
|
||||||
|
}
|
||||||
|
RIPEMD160.prototype.reset = function () {
|
||||||
|
this.MDbuf = [];
|
||||||
|
this.MDbuf[0] = 1732584193;
|
||||||
|
this.MDbuf[1] = -271733879;
|
||||||
|
this.MDbuf[2] = -1732584194;
|
||||||
|
this.MDbuf[3] = 271733878;
|
||||||
|
this.MDbuf[4] = -1009589776;
|
||||||
|
this.working = new Int32Array(16);
|
||||||
|
this.working_ptr = 0;
|
||||||
|
this.msglen = 0;
|
||||||
|
};
|
||||||
|
RIPEMD160.prototype.compress = function (X) {
|
||||||
|
var index = 0;
|
||||||
|
var a;
|
||||||
|
var b;
|
||||||
|
var c;
|
||||||
|
var d;
|
||||||
|
var e;
|
||||||
|
var A;
|
||||||
|
var B;
|
||||||
|
var C;
|
||||||
|
var D;
|
||||||
|
var E;
|
||||||
|
var temp;
|
||||||
|
var s;
|
||||||
|
A = a = this.MDbuf[0];
|
||||||
|
B = b = this.MDbuf[1];
|
||||||
|
C = c = this.MDbuf[2];
|
||||||
|
D = d = this.MDbuf[3];
|
||||||
|
E = e = this.MDbuf[4];
|
||||||
|
for (; index < 16; index++) {
|
||||||
|
temp = a + (b ^ c ^ d) + X[RIPEMD160.IndexArray[0][index]];
|
||||||
|
a = e;
|
||||||
|
e = d;
|
||||||
|
d = (c << 10) | (c >>> 22);
|
||||||
|
c = b;
|
||||||
|
s = RIPEMD160.ArgArray[0][index];
|
||||||
|
b = ((temp << s) | (temp >>> (32 - s))) + a;
|
||||||
|
temp = A + (B ^ (C | ~D)) + X[RIPEMD160.IndexArray[1][index]] + 1352829926;
|
||||||
|
A = E;
|
||||||
|
E = D;
|
||||||
|
D = (C << 10) | (C >>> 22);
|
||||||
|
C = B;
|
||||||
|
s = RIPEMD160.ArgArray[1][index];
|
||||||
|
B = ((temp << s) | (temp >>> (32 - s))) + A;
|
||||||
|
}
|
||||||
|
for (; index < 32; index++) {
|
||||||
|
temp = a + ((b & c) | (~b & d)) + X[RIPEMD160.IndexArray[0][index]] + 1518500249;
|
||||||
|
a = e;
|
||||||
|
e = d;
|
||||||
|
d = (c << 10) | (c >>> 22);
|
||||||
|
c = b;
|
||||||
|
s = RIPEMD160.ArgArray[0][index];
|
||||||
|
b = ((temp << s) | (temp >>> (32 - s))) + a;
|
||||||
|
temp = A + ((B & D) | (C & ~D)) + X[RIPEMD160.IndexArray[1][index]] + 1548603684;
|
||||||
|
A = E;
|
||||||
|
E = D;
|
||||||
|
D = (C << 10) | (C >>> 22);
|
||||||
|
C = B;
|
||||||
|
s = RIPEMD160.ArgArray[1][index];
|
||||||
|
B = ((temp << s) | (temp >>> (32 - s))) + A;
|
||||||
|
}
|
||||||
|
for (; index < 48; index++) {
|
||||||
|
temp = a + ((b | ~c) ^ d) + X[RIPEMD160.IndexArray[0][index]] + 1859775393;
|
||||||
|
a = e;
|
||||||
|
e = d;
|
||||||
|
d = (c << 10) | (c >>> 22);
|
||||||
|
c = b;
|
||||||
|
s = RIPEMD160.ArgArray[0][index];
|
||||||
|
b = ((temp << s) | (temp >>> (32 - s))) + a;
|
||||||
|
temp = A + ((B | ~C) ^ D) + X[RIPEMD160.IndexArray[1][index]] + 1836072691;
|
||||||
|
A = E;
|
||||||
|
E = D;
|
||||||
|
D = (C << 10) | (C >>> 22);
|
||||||
|
C = B;
|
||||||
|
s = RIPEMD160.ArgArray[1][index];
|
||||||
|
B = ((temp << s) | (temp >>> (32 - s))) + A;
|
||||||
|
}
|
||||||
|
for (; index < 64; index++) {
|
||||||
|
temp = a + ((b & d) | (c & ~d)) + X[RIPEMD160.IndexArray[0][index]] + -1894007588;
|
||||||
|
a = e;
|
||||||
|
e = d;
|
||||||
|
d = (c << 10) | (c >>> 22);
|
||||||
|
c = b;
|
||||||
|
s = RIPEMD160.ArgArray[0][index];
|
||||||
|
b = ((temp << s) | (temp >>> (32 - s))) + a;
|
||||||
|
temp = A + ((B & C) | (~B & D)) + X[RIPEMD160.IndexArray[1][index]] + 2053994217;
|
||||||
|
A = E;
|
||||||
|
E = D;
|
||||||
|
D = (C << 10) | (C >>> 22);
|
||||||
|
C = B;
|
||||||
|
s = RIPEMD160.ArgArray[1][index];
|
||||||
|
B = ((temp << s) | (temp >>> (32 - s))) + A;
|
||||||
|
}
|
||||||
|
for (; index < 80; index++) {
|
||||||
|
temp = a + (b ^ (c | ~d)) + X[RIPEMD160.IndexArray[0][index]] + -1454113458;
|
||||||
|
a = e;
|
||||||
|
e = d;
|
||||||
|
d = (c << 10) | (c >>> 22);
|
||||||
|
c = b;
|
||||||
|
s = RIPEMD160.ArgArray[0][index];
|
||||||
|
b = ((temp << s) | (temp >>> (32 - s))) + a;
|
||||||
|
temp = A + (B ^ C ^ D) + X[RIPEMD160.IndexArray[1][index]];
|
||||||
|
A = E;
|
||||||
|
E = D;
|
||||||
|
D = (C << 10) | (C >>> 22);
|
||||||
|
C = B;
|
||||||
|
s = RIPEMD160.ArgArray[1][index];
|
||||||
|
B = ((temp << s) | (temp >>> (32 - s))) + A;
|
||||||
|
}
|
||||||
|
D += c + this.MDbuf[1];
|
||||||
|
this.MDbuf[1] = this.MDbuf[2] + d + E;
|
||||||
|
this.MDbuf[2] = this.MDbuf[3] + e + A;
|
||||||
|
this.MDbuf[3] = this.MDbuf[4] + a + B;
|
||||||
|
this.MDbuf[4] = this.MDbuf[0] + b + C;
|
||||||
|
this.MDbuf[0] = D;
|
||||||
|
};
|
||||||
|
RIPEMD160.prototype.MDfinish = function (array, lswlen, mswlen) {
|
||||||
|
var X = array;
|
||||||
|
X[(lswlen >> 2) & 15] ^= 1 << (((lswlen & 3) << 3) + 7);
|
||||||
|
if (((lswlen & 63) > 55)) {
|
||||||
|
this.compress(X);
|
||||||
|
for (var i = 0; i < 14; i++) {
|
||||||
|
X[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
X[14] = lswlen << 3;
|
||||||
|
X[15] = (lswlen >> 29) | (mswlen << 3);
|
||||||
|
this.compress(X);
|
||||||
|
};
|
||||||
|
RIPEMD160.prototype.update = function (input) {
|
||||||
|
for (var i = 0; i < input.length; i++) {
|
||||||
|
this.working[this.working_ptr >> 2] ^= input[i] << ((this.working_ptr & 3) << 3);
|
||||||
|
this.working_ptr++;
|
||||||
|
if ((this.working_ptr == 64)) {
|
||||||
|
this.compress(this.working);
|
||||||
|
for (var j = 0; j < 16; j++) {
|
||||||
|
this.working[j] = 0;
|
||||||
|
}
|
||||||
|
this.working_ptr = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.msglen += input.length;
|
||||||
|
};
|
||||||
|
RIPEMD160.prototype.digestBin = function () {
|
||||||
|
this.MDfinish(this.working, this.msglen, 0);
|
||||||
|
//var res = new Int8Array();
|
||||||
|
var res = [];
|
||||||
|
for (var i = 0; i < 20; i++) {
|
||||||
|
res[i] = ((this.MDbuf[i >> 2] >>> ((i & 3) << 3)) & 255);
|
||||||
|
}
|
||||||
|
return new Uint8Array(res);
|
||||||
|
};
|
||||||
|
RIPEMD160.prototype.digest = function (input) {
|
||||||
|
this.update(new Int8Array(input));
|
||||||
|
return this.digestBin();
|
||||||
|
};
|
||||||
|
RIPEMD160.ArgArray = [[11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8, 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12, 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5, 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12, 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6], [8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6, 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11, 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5, 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8, 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11]];
|
||||||
|
RIPEMD160.IndexArray = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8, 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12, 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2, 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13], [5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12, 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2, 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13, 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14, 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11]];
|
||||||
|
return RIPEMD160;
|
||||||
|
})();
|
||||||
|
|
||||||
|
export default RIPEMD160
|
2609
src/deps/ecbn.ts
Normal file
2609
src/deps/ecbn.ts
Normal file
File diff suppressed because it is too large
Load Diff
87
src/deps/kdf.ts
Normal file
87
src/deps/kdf.ts
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
|
||||||
|
import {bytes_to_base64 as bytesToBase64, Sha512} from 'asmcrypto.js'
|
||||||
|
import bcrypt from 'bcryptjs'
|
||||||
|
import utils from '../utils/utils'
|
||||||
|
import { crypto } from '../constants/decryptWallet'
|
||||||
|
const stringtoUTF8Array = (message)=> {
|
||||||
|
if (typeof message === 'string') {
|
||||||
|
var s = unescape(encodeURIComponent(message)) // UTF-8
|
||||||
|
message = new Uint8Array(s.length)
|
||||||
|
for (var i = 0; i < s.length; i++) {
|
||||||
|
message[i] = s.charCodeAt(i) & 0xff
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return message
|
||||||
|
}
|
||||||
|
|
||||||
|
const stringToUTF8Array=(message)=> {
|
||||||
|
console.log({message})
|
||||||
|
if (typeof message !== 'string') return message; // Assuming you still want to pass through non-string inputs unchanged
|
||||||
|
const encoder = new TextEncoder(); // TextEncoder defaults to UTF-8
|
||||||
|
return encoder.encode(message);
|
||||||
|
}
|
||||||
|
const computekdf = async (req)=> {
|
||||||
|
const { salt, key, nonce, staticSalt, staticBcryptSalt } = req
|
||||||
|
const combinedBytes = utils.appendBuffer(new Uint8Array([]), stringToUTF8Array(`${staticSalt}${key}${nonce}`))
|
||||||
|
const sha512Hash = new Sha512().process(combinedBytes).finish().result
|
||||||
|
const sha512HashBase64 = bytesToBase64(sha512Hash)
|
||||||
|
const result = bcrypt.hashSync(sha512HashBase64.substring(0, 72), staticBcryptSalt)
|
||||||
|
if(nonce === 0){
|
||||||
|
console.log({salt, key, nonce, staticSalt, staticBcryptSalt})
|
||||||
|
console.log({combinedBytes})
|
||||||
|
console.log({sha512Hash})
|
||||||
|
console.log({sha512HashBase64})
|
||||||
|
console.log({result})
|
||||||
|
|
||||||
|
}
|
||||||
|
return { key, nonce, result }
|
||||||
|
}
|
||||||
|
|
||||||
|
export const doInitWorkers = (numberOfWorkers) => {
|
||||||
|
const workers = []
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (let i = 0; i < numberOfWorkers; i++) {
|
||||||
|
workers.push({})
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return workers
|
||||||
|
}
|
||||||
|
|
||||||
|
export const kdf = async (seed, salt, threads) => {
|
||||||
|
console.log({seed, salt, threads})
|
||||||
|
const workers = threads
|
||||||
|
const salt2 = new Uint8Array(salt)
|
||||||
|
|
||||||
|
salt = new Uint8Array(salt)
|
||||||
|
console.log({salt, salt2})
|
||||||
|
const seedParts = await Promise.all(workers.map((worker, index) => {
|
||||||
|
const nonce = index
|
||||||
|
return computekdf({
|
||||||
|
key: seed,
|
||||||
|
salt,
|
||||||
|
nonce,
|
||||||
|
staticSalt: crypto.staticSalt,
|
||||||
|
staticBcryptSalt: crypto.staticBcryptSalt
|
||||||
|
}).then(data => {
|
||||||
|
console.log({data})
|
||||||
|
let jsonData
|
||||||
|
try {
|
||||||
|
jsonData = JSON.parse(data)
|
||||||
|
data = jsonData
|
||||||
|
} catch (e) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
// if (seed !== data.key) throw new Error(kst3 + seed + ' !== ' + data.key)
|
||||||
|
// if (nonce !== data.nonce) throw new Error(kst4)
|
||||||
|
return data.result
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
console.log({seedParts})
|
||||||
|
const result = new Sha512().process(stringtoUTF8Array(crypto.staticSalt + seedParts.reduce((a, c) => a + c))).finish().result
|
||||||
|
return result
|
||||||
|
}
|
2424
src/deps/nacl-fast.ts
Normal file
2424
src/deps/nacl-fast.ts
Normal file
File diff suppressed because it is too large
Load Diff
227
src/deps/ripemd160.ts
Normal file
227
src/deps/ripemd160.ts
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
// Extracted from https://github.com/crypto-browserify/ripemd160
|
||||||
|
const ARRAY16 = new Array(16);
|
||||||
|
const zl = initU8Array([
|
||||||
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||||
|
7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8,
|
||||||
|
3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12,
|
||||||
|
1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2,
|
||||||
|
4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13
|
||||||
|
]);
|
||||||
|
const zr = initU8Array([
|
||||||
|
5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12,
|
||||||
|
6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2,
|
||||||
|
15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13,
|
||||||
|
8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14,
|
||||||
|
12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11
|
||||||
|
]);
|
||||||
|
const sl = initU8Array([
|
||||||
|
11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8,
|
||||||
|
7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12,
|
||||||
|
11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5,
|
||||||
|
11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12,
|
||||||
|
9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6
|
||||||
|
]);
|
||||||
|
const sr = initU8Array([
|
||||||
|
8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6,
|
||||||
|
9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11,
|
||||||
|
9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5,
|
||||||
|
15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8,
|
||||||
|
8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11
|
||||||
|
]);
|
||||||
|
const hl = initU32Array([0x00000000, 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xa953fd4e]);
|
||||||
|
const hr = initU32Array([0x50a28be6, 0x5c4dd124, 0x6d703ef3, 0x7a6d76e9, 0x00000000]);
|
||||||
|
function rotl(x, n) {
|
||||||
|
return (x << n) | (x >>> (32 - n));
|
||||||
|
}
|
||||||
|
function fn1(a, b, c, d, e, m, k, s) {
|
||||||
|
return (rotl((a + (b ^ c ^ d) + m + k) | 0, s) + e) | 0;
|
||||||
|
}
|
||||||
|
function fn2(a, b, c, d, e, m, k, s) {
|
||||||
|
return (rotl((a + ((b & c) | ((~b) & d)) + m + k) | 0, s) + e) | 0;
|
||||||
|
}
|
||||||
|
function fn3(a, b, c, d, e, m, k, s) {
|
||||||
|
return (rotl((a + ((b | (~c)) ^ d) + m + k) | 0, s) + e) | 0;
|
||||||
|
}
|
||||||
|
function fn4(a, b, c, d, e, m, k, s) {
|
||||||
|
return (rotl((a + ((b & d) | (c & (~d))) + m + k) | 0, s) + e) | 0;
|
||||||
|
}
|
||||||
|
function fn5(a, b, c, d, e, m, k, s) {
|
||||||
|
return (rotl((a + (b ^ (c | (~d))) + m + k) | 0, s) + e) | 0;
|
||||||
|
}
|
||||||
|
function readInt32LE(buffer, offset) {
|
||||||
|
offset >>>= 0;
|
||||||
|
return (buffer[offset])
|
||||||
|
| (buffer[offset + 1] << 8)
|
||||||
|
| (buffer[offset + 2] << 16)
|
||||||
|
| (buffer[offset + 3] << 24);
|
||||||
|
}
|
||||||
|
function writeUInt32LE(buffer, value, offset) {
|
||||||
|
value = +value;
|
||||||
|
offset >>>= 0;
|
||||||
|
buffer[offset + 3] = (value >>> 24);
|
||||||
|
buffer[offset + 2] = (value >>> 16);
|
||||||
|
buffer[offset + 1] = (value >>> 8);
|
||||||
|
buffer[offset] = (value & 0xff);
|
||||||
|
return offset + 4;
|
||||||
|
}
|
||||||
|
function writeInt32LE(buffer, value, offset) {
|
||||||
|
value = +value;
|
||||||
|
offset >>>= 0;
|
||||||
|
buffer[offset] = (value & 0xff);
|
||||||
|
buffer[offset + 1] = (value >>> 8);
|
||||||
|
buffer[offset + 2] = (value >>> 16);
|
||||||
|
buffer[offset + 3] = (value >>> 24);
|
||||||
|
return offset + 4;
|
||||||
|
}
|
||||||
|
function initU32Array(data) {
|
||||||
|
if (typeof Uint32Array !== 'undefined') {
|
||||||
|
return new Uint32Array(data);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function initU8Array(data) {
|
||||||
|
if (typeof Uint8Array !== 'undefined') {
|
||||||
|
return new Uint8Array(data);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function createU8Array(size) {
|
||||||
|
if (typeof Uint8Array !== 'undefined') {
|
||||||
|
return new Uint8Array(size);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return new Array(size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export class RIPEMD160 {
|
||||||
|
constructor() {
|
||||||
|
this._block = createU8Array(64);
|
||||||
|
this._blockSize = 64;
|
||||||
|
this._blockOffset = 0;
|
||||||
|
this._length = [0, 0, 0, 0];
|
||||||
|
this._finalized = false;
|
||||||
|
this._a = 0x67452301;
|
||||||
|
this._b = 0xefcdab89;
|
||||||
|
this._c = 0x98badcfe;
|
||||||
|
this._d = 0x10325476;
|
||||||
|
this._e = 0xc3d2e1f0;
|
||||||
|
}
|
||||||
|
update(data) {
|
||||||
|
if (this._finalized)
|
||||||
|
throw new Error('Digest already called');
|
||||||
|
// consume data
|
||||||
|
const block = this._block;
|
||||||
|
let offset = 0;
|
||||||
|
while (this._blockOffset + data.length - offset >= this._blockSize) {
|
||||||
|
for (let i = this._blockOffset; i < this._blockSize;)
|
||||||
|
block[i++] = data[offset++];
|
||||||
|
this._update();
|
||||||
|
this._blockOffset = 0;
|
||||||
|
}
|
||||||
|
while (offset < data.length)
|
||||||
|
block[this._blockOffset++] = data[offset++];
|
||||||
|
// update length
|
||||||
|
for (let j = 0, carry = data.length * 8; carry > 0; ++j) {
|
||||||
|
this._length[j] += carry;
|
||||||
|
carry = (this._length[j] / 0x0100000000) | 0;
|
||||||
|
if (carry > 0)
|
||||||
|
this._length[j] -= 0x0100000000 * carry;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
_update() {
|
||||||
|
const words = ARRAY16;
|
||||||
|
for (let j = 0; j < 16; ++j) {
|
||||||
|
words[j] = readInt32LE(this._block, j * 4);
|
||||||
|
}
|
||||||
|
let al = this._a | 0;
|
||||||
|
let bl = this._b | 0;
|
||||||
|
let cl = this._c | 0;
|
||||||
|
let dl = this._d | 0;
|
||||||
|
let el = this._e | 0;
|
||||||
|
let ar = this._a | 0;
|
||||||
|
let br = this._b | 0;
|
||||||
|
let cr = this._c | 0;
|
||||||
|
let dr = this._d | 0;
|
||||||
|
let er = this._e | 0;
|
||||||
|
// computation
|
||||||
|
for (let i = 0; i < 80; i += 1) {
|
||||||
|
let tl;
|
||||||
|
let tr;
|
||||||
|
if (i < 16) {
|
||||||
|
tl = fn1(al, bl, cl, dl, el, words[zl[i]], hl[0], sl[i]);
|
||||||
|
tr = fn5(ar, br, cr, dr, er, words[zr[i]], hr[0], sr[i]);
|
||||||
|
}
|
||||||
|
else if (i < 32) {
|
||||||
|
tl = fn2(al, bl, cl, dl, el, words[zl[i]], hl[1], sl[i]);
|
||||||
|
tr = fn4(ar, br, cr, dr, er, words[zr[i]], hr[1], sr[i]);
|
||||||
|
}
|
||||||
|
else if (i < 48) {
|
||||||
|
tl = fn3(al, bl, cl, dl, el, words[zl[i]], hl[2], sl[i]);
|
||||||
|
tr = fn3(ar, br, cr, dr, er, words[zr[i]], hr[2], sr[i]);
|
||||||
|
}
|
||||||
|
else if (i < 64) {
|
||||||
|
tl = fn4(al, bl, cl, dl, el, words[zl[i]], hl[3], sl[i]);
|
||||||
|
tr = fn2(ar, br, cr, dr, er, words[zr[i]], hr[3], sr[i]);
|
||||||
|
}
|
||||||
|
else { // if (i<80) {
|
||||||
|
tl = fn5(al, bl, cl, dl, el, words[zl[i]], hl[4], sl[i]);
|
||||||
|
tr = fn1(ar, br, cr, dr, er, words[zr[i]], hr[4], sr[i]);
|
||||||
|
}
|
||||||
|
al = el;
|
||||||
|
el = dl;
|
||||||
|
dl = rotl(cl, 10);
|
||||||
|
cl = bl;
|
||||||
|
bl = tl;
|
||||||
|
ar = er;
|
||||||
|
er = dr;
|
||||||
|
dr = rotl(cr, 10);
|
||||||
|
cr = br;
|
||||||
|
br = tr;
|
||||||
|
}
|
||||||
|
// update state
|
||||||
|
const t = (this._b + cl + dr) | 0;
|
||||||
|
this._b = (this._c + dl + er) | 0;
|
||||||
|
this._c = (this._d + el + ar) | 0;
|
||||||
|
this._d = (this._e + al + br) | 0;
|
||||||
|
this._e = (this._a + bl + cr) | 0;
|
||||||
|
this._a = t;
|
||||||
|
}
|
||||||
|
digest() {
|
||||||
|
if (this._finalized) {
|
||||||
|
throw new Error('Digest already called');
|
||||||
|
}
|
||||||
|
this._finalized = true;
|
||||||
|
// create padding and handle blocks
|
||||||
|
this._block[this._blockOffset++] = 0x80;
|
||||||
|
if (this._blockOffset > 56) {
|
||||||
|
this._block.fill(0, this._blockOffset, 64);
|
||||||
|
this._update();
|
||||||
|
this._blockOffset = 0;
|
||||||
|
}
|
||||||
|
this._block.fill(0, this._blockOffset, 56);
|
||||||
|
writeUInt32LE(this._block, this._length[0], 56);
|
||||||
|
writeUInt32LE(this._block, this._length[1], 60);
|
||||||
|
this._update();
|
||||||
|
// produce result
|
||||||
|
const buffer = createU8Array(20);
|
||||||
|
writeInt32LE(buffer, this._a, 0);
|
||||||
|
writeInt32LE(buffer, this._b, 4);
|
||||||
|
writeInt32LE(buffer, this._c, 8);
|
||||||
|
writeInt32LE(buffer, this._d, 12);
|
||||||
|
writeInt32LE(buffer, this._e, 16);
|
||||||
|
// reset state
|
||||||
|
this._block.fill(0);
|
||||||
|
this._blockOffset = 0;
|
||||||
|
for (let i = 0; i < 4; ++i) {
|
||||||
|
this._length[i] = 0;
|
||||||
|
}
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default RIPEMD160;
|
68
src/index.css
Normal file
68
src/index.css
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
:root {
|
||||||
|
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||||
|
line-height: 1.5;
|
||||||
|
font-weight: 400;
|
||||||
|
|
||||||
|
color-scheme: light dark;
|
||||||
|
color: rgba(255, 255, 255, 0.87);
|
||||||
|
background-color: #242424;
|
||||||
|
|
||||||
|
font-synthesis: none;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
font-weight: 500;
|
||||||
|
color: #646cff;
|
||||||
|
text-decoration: inherit;
|
||||||
|
}
|
||||||
|
a:hover {
|
||||||
|
color: #535bf2;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
place-items: center;
|
||||||
|
min-width: 320px;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 3.2em;
|
||||||
|
line-height: 1.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
padding: 0.6em 1.2em;
|
||||||
|
font-size: 1em;
|
||||||
|
font-weight: 500;
|
||||||
|
font-family: inherit;
|
||||||
|
background-color: #1a1a1a;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-color 0.25s;
|
||||||
|
}
|
||||||
|
button:hover {
|
||||||
|
border-color: #646cff;
|
||||||
|
}
|
||||||
|
button:focus,
|
||||||
|
button:focus-visible {
|
||||||
|
outline: 4px auto -webkit-focus-ring-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
:root {
|
||||||
|
color: #213547;
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
a:hover {
|
||||||
|
color: #747bff;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
}
|
||||||
|
}
|
10
src/main.tsx
Normal file
10
src/main.tsx
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import ReactDOM from 'react-dom/client'
|
||||||
|
import App from './App.tsx'
|
||||||
|
import './index.css'
|
||||||
|
|
||||||
|
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||||
|
<React.StrictMode>
|
||||||
|
<App />
|
||||||
|
</React.StrictMode>,
|
||||||
|
)
|
38
src/transactions/PaymentTransaction.ts
Normal file
38
src/transactions/PaymentTransaction.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
|
||||||
|
import { QORT_DECIMALS } from '../constants/constants'
|
||||||
|
import TransactionBase from './TransactionBase'
|
||||||
|
|
||||||
|
export default class PaymentTransaction extends TransactionBase {
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
this.type = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
set recipient(recipient) {
|
||||||
|
this._recipient = recipient instanceof Uint8Array ? recipient : this.constructor.Base58.decode(recipient)
|
||||||
|
}
|
||||||
|
|
||||||
|
set dialogto(dialogto) {
|
||||||
|
this._dialogto = dialogto
|
||||||
|
}
|
||||||
|
|
||||||
|
set dialogamount(dialogamount) {
|
||||||
|
this._dialogamount = dialogamount
|
||||||
|
}
|
||||||
|
|
||||||
|
set amount(amount) {
|
||||||
|
this._amount = Math.round(amount * QORT_DECIMALS)
|
||||||
|
this._amountBytes = this.constructor.utils.int64ToBytes(this._amount)
|
||||||
|
}
|
||||||
|
|
||||||
|
get params() {
|
||||||
|
const params = super.params
|
||||||
|
params.push(
|
||||||
|
this._recipient,
|
||||||
|
this._amountBytes,
|
||||||
|
this._feeBytes
|
||||||
|
)
|
||||||
|
return params
|
||||||
|
}
|
||||||
|
}
|
165
src/transactions/TransactionBase.ts
Normal file
165
src/transactions/TransactionBase.ts
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
|
||||||
|
import nacl from '../deps/nacl-fast'
|
||||||
|
import Base58 from '../deps/Base58'
|
||||||
|
import utils from '../utils/utils'
|
||||||
|
import { QORT_DECIMALS, TX_TYPES } from '../constants/constants.js'
|
||||||
|
|
||||||
|
export default class TransactionBase {
|
||||||
|
static get utils() {
|
||||||
|
return utils
|
||||||
|
}
|
||||||
|
static get nacl() {
|
||||||
|
return nacl
|
||||||
|
}
|
||||||
|
static get Base58() {
|
||||||
|
return Base58
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.fee = 0
|
||||||
|
this.groupID = 0
|
||||||
|
this.timestamp = Date.now()
|
||||||
|
this.tests = [
|
||||||
|
() => {
|
||||||
|
if (!(this._type >= 1 && this._type in TX_TYPES)) {
|
||||||
|
return 'Invalid type: ' + this.type
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
if (this._fee < 0) {
|
||||||
|
return 'Invalid fee: ' + this._fee / QORT_DECIMALS
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
if (this._groupID < 0 || !Number.isInteger(this._groupID)) {
|
||||||
|
return 'Invalid groupID: ' + this._groupID
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
if (!(new Date(this._timestamp)).getTime() > 0) {
|
||||||
|
return 'Invalid timestamp: ' + this._timestamp
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
if (!(this._lastReference instanceof Uint8Array && this._lastReference.byteLength == 64)) {
|
||||||
|
if (this._lastReference == 0) {
|
||||||
|
return 'Invalid last reference. Please ensure that you have at least 0.001 QORT for the transaction fee.'
|
||||||
|
}
|
||||||
|
return 'Invalid last reference: ' + this._lastReference
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
if (!(this._keyPair)) {
|
||||||
|
return 'keyPair must be specified'
|
||||||
|
}
|
||||||
|
if (!(this._keyPair.publicKey instanceof Uint8Array && this._keyPair.publicKey.byteLength === 32)) {
|
||||||
|
return 'Invalid publicKey'
|
||||||
|
}
|
||||||
|
if (!(this._keyPair.privateKey instanceof Uint8Array && this._keyPair.privateKey.byteLength === 64)) {
|
||||||
|
return 'Invalid privateKey'
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
set keyPair(keyPair) {
|
||||||
|
this._keyPair = keyPair
|
||||||
|
}
|
||||||
|
|
||||||
|
set type(type) {
|
||||||
|
this.typeText = TX_TYPES[type]
|
||||||
|
this._type = type
|
||||||
|
this._typeBytes = this.constructor.utils.int32ToBytes(this._type)
|
||||||
|
}
|
||||||
|
|
||||||
|
set groupID(groupID) {
|
||||||
|
this._groupID = groupID
|
||||||
|
this._groupIDBytes = this.constructor.utils.int32ToBytes(this._groupID)
|
||||||
|
}
|
||||||
|
|
||||||
|
set timestamp(timestamp) {
|
||||||
|
this._timestamp = timestamp
|
||||||
|
this._timestampBytes = this.constructor.utils.int64ToBytes(this._timestamp)
|
||||||
|
}
|
||||||
|
|
||||||
|
set fee(fee) {
|
||||||
|
this._fee = fee * QORT_DECIMALS
|
||||||
|
this._feeBytes = this.constructor.utils.int64ToBytes(this._fee)
|
||||||
|
}
|
||||||
|
|
||||||
|
set lastReference(lastReference) {
|
||||||
|
this._lastReference = lastReference instanceof Uint8Array ? lastReference : this.constructor.Base58.decode(lastReference)
|
||||||
|
}
|
||||||
|
|
||||||
|
get params() {
|
||||||
|
return [
|
||||||
|
this._typeBytes,
|
||||||
|
this._timestampBytes,
|
||||||
|
this._groupIDBytes,
|
||||||
|
this._lastReference,
|
||||||
|
this._keyPair.publicKey
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
get signedBytes() {
|
||||||
|
if (!this._signedBytes) {
|
||||||
|
this.sign()
|
||||||
|
}
|
||||||
|
return this._signedBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
validParams() {
|
||||||
|
let finalResult = {
|
||||||
|
valid: true
|
||||||
|
}
|
||||||
|
this.tests.some(test => {
|
||||||
|
const result = test()
|
||||||
|
if (result !== true) {
|
||||||
|
finalResult = {
|
||||||
|
valid: false,
|
||||||
|
message: result
|
||||||
|
}
|
||||||
|
return true // exists the loop
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return finalResult
|
||||||
|
}
|
||||||
|
|
||||||
|
generateBase() {
|
||||||
|
const isValid = this.validParams()
|
||||||
|
if (!isValid.valid) {
|
||||||
|
throw new Error(isValid.message)
|
||||||
|
}
|
||||||
|
let result = new Uint8Array()
|
||||||
|
|
||||||
|
this.params.forEach(item => {
|
||||||
|
result = this.constructor.utils.appendBuffer(result, item)
|
||||||
|
})
|
||||||
|
|
||||||
|
this._base = result
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
sign() {
|
||||||
|
if (!this._keyPair) {
|
||||||
|
throw new Error('keyPair not defined')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._base) {
|
||||||
|
this.generateBase()
|
||||||
|
}
|
||||||
|
|
||||||
|
this._signature = this.constructor.nacl.sign.detached(this._base, this._keyPair.privateKey)
|
||||||
|
|
||||||
|
this._signedBytes = this.constructor.utils.appendBuffer(this._base, this._signature)
|
||||||
|
|
||||||
|
return this._signature
|
||||||
|
}
|
||||||
|
}
|
20
src/transactions/transactions.ts
Normal file
20
src/transactions/transactions.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
|
||||||
|
import PaymentTransaction from './PaymentTransaction.js'
|
||||||
|
|
||||||
|
|
||||||
|
export const transactionTypes = {
|
||||||
|
2: PaymentTransaction,
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const createTransaction = (type, keyPair, params) => {
|
||||||
|
const tx = new transactionTypes[type]()
|
||||||
|
tx.keyPair = keyPair
|
||||||
|
Object.keys(params).forEach(param => {
|
||||||
|
tx[param] = params[param]
|
||||||
|
})
|
||||||
|
|
||||||
|
return tx
|
||||||
|
}
|
28
src/utils/chromeStorage.ts
Normal file
28
src/utils/chromeStorage.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
|
||||||
|
export const storeWalletInfo = (payload: any)=> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
chrome.storage.local.set({walletInfo: payload}, () => {
|
||||||
|
if (chrome.runtime.lastError) {
|
||||||
|
reject(new Error('Error saving data'));
|
||||||
|
} else {
|
||||||
|
resolve('Data saved successfully');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getWalletInfo = (): Promise<any> => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
chrome.storage.local.get(['walletInfo'], (result) => {
|
||||||
|
if (chrome.runtime.lastError) {
|
||||||
|
reject(new Error('Error retrieving data'));
|
||||||
|
} else if (result.walletInfo) {
|
||||||
|
resolve(result.walletInfo as any);
|
||||||
|
} else {
|
||||||
|
reject(new Error('No wallet info found'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
26
src/utils/decryptWallet.ts
Normal file
26
src/utils/decryptWallet.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
|
||||||
|
import { crypto } from '../constants/decryptWallet'
|
||||||
|
import Base58 from '../deps/Base58'
|
||||||
|
import {AES_CBC, HmacSha512} from 'asmcrypto.js'
|
||||||
|
import { doInitWorkers, kdf } from '../deps/kdf'
|
||||||
|
|
||||||
|
|
||||||
|
export const decryptStoredWallet = async (password, wallet) => {
|
||||||
|
const threads = doInitWorkers(crypto.kdfThreads)
|
||||||
|
const encryptedSeedBytes = Base58.decode(wallet.encryptedSeed)
|
||||||
|
const iv = Base58.decode(wallet.iv)
|
||||||
|
const salt = Base58.decode(wallet.salt)
|
||||||
|
|
||||||
|
const key = await kdf(password, salt, threads)
|
||||||
|
console.log('key2', key)
|
||||||
|
const encryptionKey = key.slice(0, 32)
|
||||||
|
const macKey = key.slice(32, 63)
|
||||||
|
const mac = new HmacSha512(macKey).process(encryptedSeedBytes).finish().result
|
||||||
|
console.log(Base58.encode(mac), wallet.mac)
|
||||||
|
if (Base58.encode(mac) !== wallet.mac) {
|
||||||
|
throw new Error("Incorrect password")
|
||||||
|
}
|
||||||
|
const decryptedBytes = AES_CBC.decrypt(encryptedSeedBytes, encryptionKey, false, iv)
|
||||||
|
return decryptedBytes
|
||||||
|
}
|
112
src/utils/generateWallet/generateWallet.ts
Normal file
112
src/utils/generateWallet/generateWallet.ts
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
|
||||||
|
import { crypto, walletVersion } from '../../constants/decryptWallet';
|
||||||
|
import { doInitWorkers, kdf } from '../../deps/kdf';
|
||||||
|
import PhraseWallet from './phrase-wallet';
|
||||||
|
import * as WORDLISTS from './wordlists';
|
||||||
|
|
||||||
|
export function generateRandomSentence(template = 'adverb verb noun adjective noun adverb verb noun adjective noun adjective verbed adjective noun', maxWordLength = 0, capitalize = true) {
|
||||||
|
const partsOfSpeechMap = {
|
||||||
|
'noun': 'nouns',
|
||||||
|
'adverb': 'adverbs',
|
||||||
|
'adv': 'adverbs',
|
||||||
|
'verb': 'verbs',
|
||||||
|
'interjection': 'interjections',
|
||||||
|
'adjective': 'adjectives',
|
||||||
|
'adj': 'adjectives',
|
||||||
|
'verbed': 'verbed'
|
||||||
|
};
|
||||||
|
|
||||||
|
let _wordlists = WORDLISTS;
|
||||||
|
|
||||||
|
function _RNG(entropy) {
|
||||||
|
if (entropy > 1074) {
|
||||||
|
throw new Error('Javascript can not handle that much entropy!');
|
||||||
|
}
|
||||||
|
let randNum = 0;
|
||||||
|
const crypto = window.crypto || window.msCrypto;
|
||||||
|
|
||||||
|
if (crypto) {
|
||||||
|
const entropy256 = Math.ceil(entropy / 8);
|
||||||
|
let buffer = new Uint8Array(entropy256);
|
||||||
|
crypto.getRandomValues(buffer);
|
||||||
|
randNum = buffer.reduce((num, value) => num * 256 + value, 0) / Math.pow(256, entropy256);
|
||||||
|
} else {
|
||||||
|
console.warn('Secure RNG not found. Using Math.random');
|
||||||
|
randNum = Math.random();
|
||||||
|
}
|
||||||
|
return randNum;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _capitalize(str) {
|
||||||
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getWord(partOfSpeech) {
|
||||||
|
let words = _wordlists[partsOfSpeechMap[partOfSpeech]];
|
||||||
|
if (maxWordLength) {
|
||||||
|
words = words.filter(word => word.length <= maxWordLength);
|
||||||
|
}
|
||||||
|
const requiredEntropy = Math.log(words.length) / Math.log(2);
|
||||||
|
const index = Math.floor(_RNG(requiredEntropy) * words.length);
|
||||||
|
return words[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
function parse(template) {
|
||||||
|
return template.split(/\s+/).map(token => {
|
||||||
|
const match = token.match(/^(\w+)(.*)$/);
|
||||||
|
if (!match) return token; // No match, return original token
|
||||||
|
|
||||||
|
const [ , partOfSpeech, rest ] = match;
|
||||||
|
if (partsOfSpeechMap[partOfSpeech]) {
|
||||||
|
let word = getWord(partOfSpeech);
|
||||||
|
if (capitalize && token === token[0].toUpperCase() + token.slice(1).toLowerCase()) {
|
||||||
|
word = _capitalize(word);
|
||||||
|
}
|
||||||
|
return word + rest;
|
||||||
|
}
|
||||||
|
|
||||||
|
return token;
|
||||||
|
}).join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
return parse(template);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createAccount = async()=> {
|
||||||
|
const generatedSeedPhrase = generateRandomSentence()
|
||||||
|
const threads = doInitWorkers(crypto.kdfThreads)
|
||||||
|
|
||||||
|
const seed = await kdf(generatedSeedPhrase, void 0, threads)
|
||||||
|
const wallet = new PhraseWallet(seed, walletVersion)
|
||||||
|
return wallet
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export const saveFileToDisk= async(data, qortAddress) => {
|
||||||
|
try {
|
||||||
|
console.log({data, qortAddress})
|
||||||
|
const dataString = JSON.stringify(data)
|
||||||
|
const blob = new Blob([dataString], { type: 'text/plain;charset=utf-8' })
|
||||||
|
const fileName = "qortal_backup_" + qortAddress + ".json"
|
||||||
|
const fileHandle = await self.showSaveFilePicker({
|
||||||
|
suggestedName: fileName,
|
||||||
|
types: [{
|
||||||
|
description: "File",
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
const writeFile = async (fileHandle, contents) => {
|
||||||
|
const writable = await fileHandle.createWritable()
|
||||||
|
await writable.write(contents)
|
||||||
|
await writable.close()
|
||||||
|
}
|
||||||
|
writeFile(fileHandle, blob).then(() => console.log("FILE SAVED"))
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.log({error})
|
||||||
|
if (error.name === 'AbortError') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
FileSaver.saveAs(blob, fileName)
|
||||||
|
}
|
||||||
|
}
|
210
src/utils/generateWallet/phrase-wallet.ts
Normal file
210
src/utils/generateWallet/phrase-wallet.ts
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
/*
|
||||||
|
Copyright 2017-2018 @ irontiga and vbcs (original developer)
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Base58 from '../../deps/Base58'
|
||||||
|
import {Sha256, Sha512} from 'asmcrypto.js'
|
||||||
|
import nacl from '../../deps/nacl-fast'
|
||||||
|
import utils from '../../utils/utils'
|
||||||
|
|
||||||
|
import {generateSaveWalletData} from './storeWallet.js'
|
||||||
|
|
||||||
|
import publicKeyToAddress from './publicKeyToAddress'
|
||||||
|
import AltcoinHDWallet from "../../deps/AltcoinHDWallet"
|
||||||
|
|
||||||
|
export default class PhraseWallet {
|
||||||
|
constructor(seed, walletVersion) {
|
||||||
|
|
||||||
|
this._walletVersion = walletVersion || 2
|
||||||
|
this.seed = seed
|
||||||
|
|
||||||
|
this.savedSeedData = {}
|
||||||
|
this.hasBeenSaved = false
|
||||||
|
}
|
||||||
|
|
||||||
|
set seed(seed) {
|
||||||
|
this._byteSeed = seed
|
||||||
|
this._base58Seed = Base58.encode(seed)
|
||||||
|
|
||||||
|
this._addresses = []
|
||||||
|
|
||||||
|
this.genAddress(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
getAddress(nonce) {
|
||||||
|
return this._addresses[nonce]
|
||||||
|
}
|
||||||
|
|
||||||
|
get addresses() {
|
||||||
|
return this._addresses
|
||||||
|
}
|
||||||
|
|
||||||
|
get addressIDs() {
|
||||||
|
return this._addresses.map(addr => {
|
||||||
|
return addr.address
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
get seed() {
|
||||||
|
return this._byteSeed
|
||||||
|
}
|
||||||
|
|
||||||
|
addressExists(nonce) {
|
||||||
|
return this._addresses[nonce] != undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
_genAddressSeed(seed) {
|
||||||
|
let newSeed = new Sha512().process(seed).finish().result
|
||||||
|
newSeed = new Sha512().process(utils.appendBuffer(newSeed, seed)).finish().result
|
||||||
|
return newSeed
|
||||||
|
}
|
||||||
|
|
||||||
|
genAddress(nonce) {
|
||||||
|
if (nonce >= this._addresses.length) {
|
||||||
|
this._addresses.length = nonce + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.addressExists(nonce)) {
|
||||||
|
return this.addresses[nonce]
|
||||||
|
}
|
||||||
|
|
||||||
|
const nonceBytes = utils.int32ToBytes(nonce)
|
||||||
|
|
||||||
|
let addrSeed = new Uint8Array()
|
||||||
|
addrSeed = utils.appendBuffer(addrSeed, nonceBytes)
|
||||||
|
addrSeed = utils.appendBuffer(addrSeed, this._byteSeed)
|
||||||
|
addrSeed = utils.appendBuffer(addrSeed, nonceBytes)
|
||||||
|
|
||||||
|
if (this._walletVersion == 1) {
|
||||||
|
addrSeed = new Sha256().process(
|
||||||
|
new Sha256()
|
||||||
|
.process(addrSeed)
|
||||||
|
.finish()
|
||||||
|
.result
|
||||||
|
).finish().result
|
||||||
|
|
||||||
|
addrSeed = this._byteSeed
|
||||||
|
} else {
|
||||||
|
addrSeed = this._genAddressSeed(addrSeed).slice(0, 32)
|
||||||
|
}
|
||||||
|
|
||||||
|
const addrKeyPair = nacl.sign.keyPair.fromSeed(new Uint8Array(addrSeed));
|
||||||
|
|
||||||
|
const address = publicKeyToAddress(addrKeyPair.publicKey);
|
||||||
|
const qoraAddress = publicKeyToAddress(addrKeyPair.publicKey, true);
|
||||||
|
|
||||||
|
// Create Bitcoin HD Wallet
|
||||||
|
const btcSeed = [...addrSeed];
|
||||||
|
const btcWallet = new AltcoinHDWallet({
|
||||||
|
mainnet: {
|
||||||
|
private: 0x0488ADE4,
|
||||||
|
public: 0x0488B21E,
|
||||||
|
prefix: 0
|
||||||
|
},
|
||||||
|
testnet: {
|
||||||
|
private: 0x04358394,
|
||||||
|
public: 0x043587CF,
|
||||||
|
prefix: 0x6F
|
||||||
|
}
|
||||||
|
}).createWallet(new Uint8Array(btcSeed), false);
|
||||||
|
|
||||||
|
// Create Litecoin HD Wallet
|
||||||
|
const ltcSeed = [...addrSeed];
|
||||||
|
const ltcWallet = new AltcoinHDWallet({
|
||||||
|
mainnet: {
|
||||||
|
private: 0x0488ADE4,
|
||||||
|
public: 0x0488B21E,
|
||||||
|
prefix: 0x30
|
||||||
|
},
|
||||||
|
testnet: {
|
||||||
|
private: 0x04358394,
|
||||||
|
public: 0x043587CF,
|
||||||
|
prefix: 0x6F
|
||||||
|
}
|
||||||
|
}).createWallet(new Uint8Array(ltcSeed), false, 'LTC');
|
||||||
|
|
||||||
|
// Create Dogecoin HD Wallet
|
||||||
|
const dogeSeed = [...addrSeed];
|
||||||
|
const dogeWallet = new AltcoinHDWallet({
|
||||||
|
mainnet: {
|
||||||
|
private: 0x02FAC398,
|
||||||
|
public: 0x02FACAFD,
|
||||||
|
prefix: 0x1E
|
||||||
|
},
|
||||||
|
testnet: {
|
||||||
|
private: 0x04358394,
|
||||||
|
public: 0x043587CF,
|
||||||
|
prefix: 0x71
|
||||||
|
}
|
||||||
|
}).createWallet(new Uint8Array(dogeSeed), false, 'DOGE');
|
||||||
|
|
||||||
|
// Create Digibyte HD Wallet
|
||||||
|
const dgbSeed = [...addrSeed];
|
||||||
|
const dgbWallet = new AltcoinHDWallet({
|
||||||
|
mainnet: {
|
||||||
|
private: 0x0488ADE4,
|
||||||
|
public: 0x0488B21E,
|
||||||
|
prefix: 0x1E
|
||||||
|
},
|
||||||
|
testnet: {
|
||||||
|
private: 0x04358394,
|
||||||
|
public: 0x043587CF,
|
||||||
|
prefix: 0x7E
|
||||||
|
}
|
||||||
|
}).createWallet(new Uint8Array(dgbSeed), false, 'DGB');
|
||||||
|
|
||||||
|
// Create Ravencoin HD Wallet
|
||||||
|
const rvnSeed = [...addrSeed];
|
||||||
|
const rvnWallet = new AltcoinHDWallet({
|
||||||
|
mainnet: {
|
||||||
|
private: 0x0488ADE4,
|
||||||
|
public: 0x0488B21E,
|
||||||
|
prefix: 0x3C
|
||||||
|
},
|
||||||
|
testnet: {
|
||||||
|
private: 0x04358394,
|
||||||
|
public: 0x043587CF,
|
||||||
|
prefix: 0x6F
|
||||||
|
}
|
||||||
|
}).createWallet(new Uint8Array(rvnSeed), false, 'RVN');
|
||||||
|
|
||||||
|
// Create Pirate Chain HD Wallet
|
||||||
|
const arrrSeed = [...addrSeed];
|
||||||
|
const arrrWallet = new AltcoinHDWallet({
|
||||||
|
mainnet: {
|
||||||
|
private: 0x0488ADE4,
|
||||||
|
public: 0x0488B21E,
|
||||||
|
prefix: [0x16, 0x9A]
|
||||||
|
},
|
||||||
|
testnet: {
|
||||||
|
private: 0x04358394,
|
||||||
|
public: 0x043587CF,
|
||||||
|
prefix: [0x14, 0x51]
|
||||||
|
}
|
||||||
|
}).createWallet(new Uint8Array(arrrSeed), false, 'ARRR');
|
||||||
|
|
||||||
|
this._addresses[nonce] = {
|
||||||
|
address,
|
||||||
|
btcWallet,
|
||||||
|
ltcWallet,
|
||||||
|
dogeWallet,
|
||||||
|
dgbWallet,
|
||||||
|
rvnWallet,
|
||||||
|
arrrWallet,
|
||||||
|
qoraAddress,
|
||||||
|
keyPair: {
|
||||||
|
publicKey: addrKeyPair.publicKey,
|
||||||
|
privateKey: addrKeyPair.secretKey
|
||||||
|
},
|
||||||
|
base58PublicKey: Base58.encode(addrKeyPair.publicKey),
|
||||||
|
seed: addrSeed,
|
||||||
|
nonce: nonce
|
||||||
|
}
|
||||||
|
return this._addresses[nonce]
|
||||||
|
}
|
||||||
|
|
||||||
|
generateSaveWalletData(...args) {
|
||||||
|
return generateSaveWalletData(this, ...args)
|
||||||
|
}
|
||||||
|
}
|
36
src/utils/generateWallet/publicKeyToAddress.ts
Normal file
36
src/utils/generateWallet/publicKeyToAddress.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import Base58 from '../../deps/Base58'
|
||||||
|
import BROKEN_RIPEMD160 from '../../deps/broken-ripemd160'
|
||||||
|
import RIPEMD160 from '../../deps/ripemd160'
|
||||||
|
import utils from '../../utils/utils'
|
||||||
|
import {Buffer} from 'buffer'
|
||||||
|
import {Sha256} from 'asmcrypto.js'
|
||||||
|
import { ADDRESS_VERSION } from '../../constants/general.js'
|
||||||
|
|
||||||
|
const repeatSHA256 = (passphrase, hashes) => {
|
||||||
|
let hash = passphrase
|
||||||
|
for (let i = 0; i < hashes; i++) {
|
||||||
|
hash = new Sha256().process(hash).finish().result
|
||||||
|
}
|
||||||
|
return hash
|
||||||
|
}
|
||||||
|
|
||||||
|
const publicKeyToAddress = (publicKey, qora = false) => {
|
||||||
|
const publicKeySha256 = new Sha256().process(publicKey).finish().result
|
||||||
|
const _publicKeyHash = qora ? new BROKEN_RIPEMD160().digest(publicKeySha256) : new RIPEMD160().update(Buffer.from(publicKeySha256)).digest('hex')
|
||||||
|
const publicKeyHash = qora ? _publicKeyHash : _publicKeyHash
|
||||||
|
|
||||||
|
let address = new Uint8Array()
|
||||||
|
|
||||||
|
address = utils.appendBuffer(address, [ADDRESS_VERSION])
|
||||||
|
address = utils.appendBuffer(address, publicKeyHash)
|
||||||
|
|
||||||
|
const checkSum = repeatSHA256(address, 2)
|
||||||
|
address = utils.appendBuffer(address, checkSum.subarray(0, 4))
|
||||||
|
|
||||||
|
address = Base58.encode(address)
|
||||||
|
|
||||||
|
return address
|
||||||
|
}
|
||||||
|
|
||||||
|
export default publicKeyToAddress
|
32
src/utils/generateWallet/storeWallet.ts
Normal file
32
src/utils/generateWallet/storeWallet.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
|
||||||
|
import {AES_CBC, HmacSha512} from 'asmcrypto.js'
|
||||||
|
|
||||||
|
import Base58 from '../../deps/Base58'
|
||||||
|
import { doInitWorkers, kdf } from '../../deps/kdf.js'
|
||||||
|
import { crypto as cryptoVals } from '../../constants/decryptWallet.js'
|
||||||
|
|
||||||
|
const getRandomValues = crypto ? crypto.getRandomValues.bind(crypto) : msCrypto.getRandomValues.bind(msCrypto)
|
||||||
|
console.log({getRandomValues})
|
||||||
|
export const generateSaveWalletData = async (wallet, password, kdfThreads) => {
|
||||||
|
const threads = doInitWorkers(cryptoVals.kdfThreads)
|
||||||
|
|
||||||
|
let iv = new Uint8Array(16)
|
||||||
|
getRandomValues(iv)
|
||||||
|
let salt = new Uint8Array(32)
|
||||||
|
getRandomValues(salt)
|
||||||
|
const key = await kdf(password, salt, threads)
|
||||||
|
const encryptionKey = key.slice(0, 32)
|
||||||
|
const macKey = key.slice(32, 63)
|
||||||
|
const encryptedSeed = AES_CBC.encrypt(wallet._byteSeed, encryptionKey, false, iv)
|
||||||
|
const mac = new HmacSha512(macKey).process(encryptedSeed).finish().result
|
||||||
|
return {
|
||||||
|
address0: wallet._addresses[0].address,
|
||||||
|
encryptedSeed: Base58.encode(encryptedSeed),
|
||||||
|
salt: Base58.encode(salt),
|
||||||
|
iv: Base58.encode(iv),
|
||||||
|
version: wallet._walletVersion,
|
||||||
|
mac: Base58.encode(mac),
|
||||||
|
kdfThreads
|
||||||
|
}
|
||||||
|
}
|
37
src/utils/generateWallet/verb-past-tense.ts
Normal file
37
src/utils/generateWallet/verb-past-tense.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// Sourced from https://gist.github.com/letsgetrandy/1e05a68ea74ba6736eb5
|
||||||
|
// @ts-nocheck
|
||||||
|
|
||||||
|
export const EXCEPTIONS = {
|
||||||
|
'are': 'were',
|
||||||
|
'eat': 'ate',
|
||||||
|
'go': 'went',
|
||||||
|
'have': 'had',
|
||||||
|
'inherit': 'inherited',
|
||||||
|
'is': 'was',
|
||||||
|
'run': 'ran',
|
||||||
|
'sit': 'sat',
|
||||||
|
'visit': 'visited'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getPastTense = (verb, exceptions = EXCEPTIONS) => {
|
||||||
|
if (exceptions[verb]) {
|
||||||
|
return exceptions[verb]
|
||||||
|
}
|
||||||
|
if ((/e$/i).test(verb)) {
|
||||||
|
return verb + 'd'
|
||||||
|
}
|
||||||
|
if ((/[aeiou]c$/i).test(verb)) {
|
||||||
|
return verb + 'ked'
|
||||||
|
}
|
||||||
|
// for american english only
|
||||||
|
if ((/el$/i).test(verb)) {
|
||||||
|
return verb + 'ed'
|
||||||
|
}
|
||||||
|
if ((/[aeio][aeiou][dlmnprst]$/).test(verb)) {
|
||||||
|
return verb + 'ed'
|
||||||
|
}
|
||||||
|
if ((/[aeiou][bdglmnprst]$/i).test(verb)) {
|
||||||
|
return verb.replace(/(.+[aeiou])([bdglmnprst])/, '$1$2$2ed')
|
||||||
|
}
|
||||||
|
return verb + 'ed'
|
||||||
|
}
|
29
src/utils/generateWallet/wordlists.ts
Normal file
29
src/utils/generateWallet/wordlists.ts
Normal file
File diff suppressed because one or more lines are too long
56
src/utils/utils.ts
Normal file
56
src/utils/utils.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
const utils = {
|
||||||
|
int32ToBytes (word) {
|
||||||
|
var byteArray = []
|
||||||
|
for (var b = 0; b < 32; b += 8) {
|
||||||
|
byteArray.push((word >>> (24 - b % 32)) & 0xFF)
|
||||||
|
}
|
||||||
|
return byteArray
|
||||||
|
},
|
||||||
|
|
||||||
|
stringtoUTF8Array (message) {
|
||||||
|
if (typeof message === 'string') {
|
||||||
|
var s = unescape(encodeURIComponent(message)) // UTF-8
|
||||||
|
message = new Uint8Array(s.length)
|
||||||
|
for (var i = 0; i < s.length; i++) {
|
||||||
|
message[i] = s.charCodeAt(i) & 0xff
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return message
|
||||||
|
},
|
||||||
|
// ...buffers then buffers.foreach and append to buffer1
|
||||||
|
appendBuffer (buffer1, buffer2) {
|
||||||
|
buffer1 = new Uint8Array(buffer1)
|
||||||
|
buffer2 = new Uint8Array(buffer2)
|
||||||
|
console.log('buffer1.byteLength ', buffer1.byteLength , buffer2.byteLength)
|
||||||
|
const tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength)
|
||||||
|
tmp.set(buffer1, 0)
|
||||||
|
tmp.set(buffer2, buffer1.byteLength)
|
||||||
|
return tmp
|
||||||
|
},
|
||||||
|
|
||||||
|
int64ToBytes (int64) {
|
||||||
|
// we want to represent the input as a 8-bytes array
|
||||||
|
var byteArray = [0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
|
|
||||||
|
for (var index = 0; index < byteArray.length; index++) {
|
||||||
|
var byte = int64 & 0xff
|
||||||
|
byteArray[byteArray.length - index - 1] = byte
|
||||||
|
int64 = (int64 - byte) / 256
|
||||||
|
}
|
||||||
|
|
||||||
|
return byteArray
|
||||||
|
},
|
||||||
|
|
||||||
|
equal (buf1, buf2) {
|
||||||
|
if (buf1.byteLength != buf2.byteLength) return false
|
||||||
|
var dv1 = new Uint8Array(buf1)
|
||||||
|
var dv2 = new Uint8Array(buf2)
|
||||||
|
for (var i = 0; i != buf1.byteLength; i++) {
|
||||||
|
if (dv1[i] != dv2[i]) return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default utils
|
12
src/utils/validateAddress.ts
Normal file
12
src/utils/validateAddress.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
|
||||||
|
import Base58 from "../deps/Base58"
|
||||||
|
|
||||||
|
export const validateAddress = (address) => {
|
||||||
|
const decodePubKey = Base58.decode(address)
|
||||||
|
|
||||||
|
if (!(decodePubKey instanceof Uint8Array && decodePubKey.length == 25)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
1
src/vite-env.d.ts
vendored
Normal file
1
src/vite-env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/// <reference types="vite/client" />
|
25
tsconfig.json
Normal file
25
tsconfig.json
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||||
|
"module": "ESNext",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
|
||||||
|
/* Bundler mode */
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
|
||||||
|
/* Linting */
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": false,
|
||||||
|
"noUnusedParameters": false,
|
||||||
|
"noFallthroughCasesInSwitch": true
|
||||||
|
},
|
||||||
|
"include": ["src"],
|
||||||
|
"references": [{ "path": "./tsconfig.node.json" }]
|
||||||
|
}
|
11
tsconfig.node.json
Normal file
11
tsconfig.node.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"strict": true
|
||||||
|
},
|
||||||
|
"include": ["vite.config.ts"]
|
||||||
|
}
|
23
vite.config.ts
Normal file
23
vite.config.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { defineConfig } from 'vite';
|
||||||
|
import react from '@vitejs/plugin-react';
|
||||||
|
// Import path module for resolving file paths
|
||||||
|
import { resolve } from 'path';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react()],
|
||||||
|
build: {
|
||||||
|
rollupOptions: {
|
||||||
|
// Specify multiple entry points for Rollup
|
||||||
|
input: {
|
||||||
|
index: resolve(__dirname, 'index.html'), // Main entry for your React app
|
||||||
|
background: resolve(__dirname, 'src/background.ts'), // Separate entry for the background script
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
// Adjust the output settings if necessary
|
||||||
|
entryFileNames: `[name].js`,
|
||||||
|
chunkFileNames: `[name].js`,
|
||||||
|
assetFileNames: `[name].[ext]`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user