Browse Source

first commit

pull/1/head
PhilReact 5 months ago
commit
0b8db25b24
  1. 18
      .eslintrc.cjs
  2. 24
      .gitignore
  3. 30
      README.md
  4. 13
      index.html
  5. 4060
      package-lock.json
  6. 38
      package.json
  7. 152
      public/content-script.js
  8. 25
      public/manifest.json
  9. BIN
      public/qort.png
  10. 1
      public/vite.svg
  11. 42
      src/App.css
  12. 735
      src/App.tsx
  13. 1
      src/assets/react.svg
  14. 797
      src/background.ts
  15. 175
      src/constants/constants.ts
  16. 11
      src/constants/decryptWallet.ts
  17. 5
      src/constants/general.ts
  18. 864
      src/deps/AltcoinHDWallet.ts
  19. 103
      src/deps/Base58.ts
  20. 182
      src/deps/broken-ripemd160.ts
  21. 2609
      src/deps/ecbn.ts
  22. 87
      src/deps/kdf.ts
  23. 2424
      src/deps/nacl-fast.ts
  24. 227
      src/deps/ripemd160.ts
  25. 68
      src/index.css
  26. 10
      src/main.tsx
  27. 38
      src/transactions/PaymentTransaction.ts
  28. 165
      src/transactions/TransactionBase.ts
  29. 20
      src/transactions/transactions.ts
  30. 28
      src/utils/chromeStorage.ts
  31. 26
      src/utils/decryptWallet.ts
  32. 112
      src/utils/generateWallet/generateWallet.ts
  33. 210
      src/utils/generateWallet/phrase-wallet.ts
  34. 36
      src/utils/generateWallet/publicKeyToAddress.ts
  35. 32
      src/utils/generateWallet/storeWallet.ts
  36. 37
      src/utils/generateWallet/verb-past-tense.ts
  37. 29
      src/utils/generateWallet/wordlists.ts
  38. 56
      src/utils/utils.ts
  39. 12
      src/utils/validateAddress.ts
  40. 1
      src/vite-env.d.ts
  41. 25
      tsconfig.json
  42. 11
      tsconfig.node.json
  43. 23
      vite.config.ts

18
.eslintrc.cjs

@ -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

@ -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

@ -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

@ -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

File diff suppressed because it is too large Load Diff

38
package.json

@ -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

@ -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

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

1
public/vite.svg

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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 vendored

@ -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 vendored

@ -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 vendored

@ -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 vendored

File diff suppressed because it is too large Load Diff

87
src/deps/kdf.ts vendored

@ -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 vendored

File diff suppressed because it is too large Load Diff

227
src/deps/ripemd160.ts vendored

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

File diff suppressed because one or more lines are too long

56
src/utils/utils.ts

@ -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

@ -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

@ -0,0 +1 @@
/// <reference types="vite/client" />

25
tsconfig.json

@ -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

@ -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

@ -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…
Cancel
Save