PhilReact
5 months ago
commit
0b8db25b24
43 changed files with 13562 additions and 0 deletions
@ -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 }, |
||||||
|
], |
||||||
|
}, |
||||||
|
} |
@ -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? |
@ -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 |
@ -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> |
@ -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" |
||||||
|
} |
||||||
|
} |
@ -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...
|
||||||
|
}); |
@ -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"] |
||||||
|
} |
||||||
|
] |
||||||
|
} |
After Width: | Height: | Size: 1.9 KiB |
@ -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; |
||||||
|
} |
@ -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; |
@ -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); |
||||||
|
} |
||||||
|
); |
||||||
|
}); |
@ -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 } |
@ -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 |
@ -0,0 +1,5 @@ |
|||||||
|
// Qortal 8 decimals
|
||||||
|
export const QORT_DECIMALS = 1e8 |
||||||
|
|
||||||
|
// Q for Qortal
|
||||||
|
export const ADDRESS_VERSION = 58 |
@ -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 |
||||||
|
} |
||||||
|
} |
@ -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; |
@ -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 |
@ -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 |
||||||
|
} |
File diff suppressed because it is too large
Load Diff
@ -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; |
@ -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; |
||||||
|
} |
||||||
|
} |
@ -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>, |
||||||
|
) |
@ -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 |
||||||
|
} |
||||||
|
} |
@ -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 |
||||||
|
} |
||||||
|
} |
@ -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 |
||||||
|
} |
@ -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')); |
||||||
|
} |
||||||
|
}); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
|
@ -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 |
||||||
|
} |
@ -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) |
||||||
|
} |
||||||
|
} |
@ -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) |
||||||
|
} |
||||||
|
} |
@ -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 |
@ -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 |
||||||
|
} |
||||||
|
} |
@ -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' |
||||||
|
} |
File diff suppressed because one or more lines are too long
@ -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 |
@ -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 |
||||||
|
} |
@ -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" }] |
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
{ |
||||||
|
"compilerOptions": { |
||||||
|
"composite": true, |
||||||
|
"skipLibCheck": true, |
||||||
|
"module": "ESNext", |
||||||
|
"moduleResolution": "bundler", |
||||||
|
"allowSyntheticDefaultImports": true, |
||||||
|
"strict": true |
||||||
|
}, |
||||||
|
"include": ["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…
Reference in new issue