mirror of
https://github.com/Qortal/chrome-extension.git
synced 2025-02-11 09:45: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