@mybucks.online/core 1.0.11 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -10
- package/index.js +40 -19
- package/package.json +3 -2
- package/test/index.test.js +100 -30
package/README.md
CHANGED
|
@@ -4,11 +4,11 @@ This is a core part of [mybucks.online](https://mybucks.online) crypto wallet, i
|
|
|
4
4
|
|
|
5
5
|
## mybucks.online
|
|
6
6
|
|
|
7
|
-
[Mybucks.online](https://mybucks.online) is a **seedless, disposable crypto wallet** designed for **speed and convenience**. It generates a private key from your
|
|
7
|
+
[Mybucks.online](https://mybucks.online) is a **seedless, disposable crypto wallet** designed for **speed and convenience**. It generates a private key from your **passphrase and PIN** using an industry-standard, verified **one-way hash function**. Your private key forms your account, allowing you to transfer, receive, and hold your crypto assets instantly.
|
|
8
8
|
|
|
9
|
-
As a hash function, the **
|
|
9
|
+
As a hash function, the **Scrypt** Key Derivation Function (KDF) increases the computational effort required to crack credentials, effectively delaying **brute-force** attacks and making them impractical.
|
|
10
10
|
|
|
11
|
-
It fully runs on your **browser side** without using any storage or invoking any 3rd-party APIs for key management. It instantly generates your private key from your
|
|
11
|
+
It fully runs on your **browser side** without using any storage or invoking any 3rd-party APIs for key management. It instantly generates your private key from your credentials input, and whenever you close or refresh, there is **no footprint**. This absolutely protects your privacy.
|
|
12
12
|
|
|
13
13
|
### Zero Footprint
|
|
14
14
|
- No servers, no databases, no storage and no tracking.
|
|
@@ -19,7 +19,7 @@ It fully runs on your **browser side** without using any storage or invoking any
|
|
|
19
19
|
### Fast and Easy
|
|
20
20
|
- No app installs, no browser extensions, no registration and no KYC.
|
|
21
21
|
- You can create or open your wallet in seconds - all you need is your browser.
|
|
22
|
-
-
|
|
22
|
+
- Passphrase is easier to handle and remember than seed phrases.
|
|
23
23
|
|
|
24
24
|
### 1-Click Gifting
|
|
25
25
|
- Stop asking your friends for their wallet addresses.
|
|
@@ -51,7 +51,7 @@ const showProgress = (p) => {
|
|
|
51
51
|
console.log(`progress: ${p * 100}%`);
|
|
52
52
|
};
|
|
53
53
|
|
|
54
|
-
const hash = await generateHash(
|
|
54
|
+
const hash = await generateHash(passphrase, pin, showProgress);
|
|
55
55
|
|
|
56
56
|
const privateKey = getEvmPrivateKey(hash);
|
|
57
57
|
console.log("Private key: ", privateKey);
|
|
@@ -66,15 +66,15 @@ console.log("TRON Address: ", address2);
|
|
|
66
66
|
### 3. Generate and parse (transfer-link's)token
|
|
67
67
|
```javascript
|
|
68
68
|
import { generateToken } from "@mybucks.online/core";
|
|
69
|
-
const token = generateToken(
|
|
69
|
+
const token = generateToken(passphrase, pin, network);
|
|
70
70
|
|
|
71
71
|
console.log("https://app.mybucks.online/#wallet=" + token);
|
|
72
72
|
```
|
|
73
73
|
|
|
74
74
|
```javascript
|
|
75
75
|
import { parseToken } from "@mybucks.online/core";
|
|
76
|
-
const [
|
|
77
|
-
console.log("Account credentials are: ",
|
|
76
|
+
const [passphrase, pin, network] = parseToken(token);
|
|
77
|
+
console.log("Account credentials are: ", passphrase, pin);
|
|
78
78
|
console.log("Network: ", network);
|
|
79
79
|
```
|
|
80
80
|
|
|
@@ -91,8 +91,8 @@ Find the docs [here](https://docs.mybucks.online).
|
|
|
91
91
|
|
|
92
92
|
- https://github.com/mybucks-online/app
|
|
93
93
|
- https://app.mybucks.online
|
|
94
|
-
|
|
95
|
-
|
|
94
|
+
passphrase: **DemoAccount5&**
|
|
95
|
+
PIN: **112324**
|
|
96
96
|
- https://app.mybucks.online/#wallet=VWnsSGRGVtb0FjY291bnQ1JgIxMTIzMjQCb3B0aW1pc20=_wNovT
|
|
97
97
|
- https://app.mybucks.online/#wallet=1jpFD8RGVtb0FjY291bnQ1JgIxMTIzMjQCYmFzZQ==fhk-GL
|
|
98
98
|
- https://codesandbox.io/p/sandbox/mybucks-online-key-generation-sandbox-lt53c3
|
package/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import { ethers } from "ethers";
|
|
|
3
3
|
import scryptJS from "scrypt-js";
|
|
4
4
|
import { nanoid } from "nanoid";
|
|
5
5
|
import { TronWeb } from "tronweb";
|
|
6
|
+
import zxcvbn from "zxcvbn";
|
|
6
7
|
|
|
7
8
|
const { scrypt } = scryptJS;
|
|
8
9
|
const abi = new ethers.AbiCoder();
|
|
@@ -15,21 +16,32 @@ const HASH_OPTIONS = {
|
|
|
15
16
|
};
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
|
-
* This function computes the scrypt hash using provided
|
|
19
|
+
* This function computes the scrypt hash using provided passphrase and pin inputs.
|
|
20
|
+
* Passphrase and pin are validated with zxcvbn; weak values are rejected (returns "").
|
|
19
21
|
*
|
|
20
|
-
* @param {*}
|
|
21
|
-
* @param {*}
|
|
22
|
+
* @param {*} passphrase - Must have zxcvbn score >= 3
|
|
23
|
+
* @param {*} pin - Must have zxcvbn score >= 1
|
|
22
24
|
* @param {*} cb a callback function designed to receive the progress updates during the scrypt hashing process.
|
|
23
|
-
* @returns hash result as string format
|
|
25
|
+
* @returns hash result as string format, or "" if passphrase/pin missing or too weak
|
|
24
26
|
*/
|
|
25
|
-
export async function generateHash(
|
|
26
|
-
if (!
|
|
27
|
+
export async function generateHash(passphrase, pin, cb = () => {}) {
|
|
28
|
+
if (!passphrase || !pin) {
|
|
27
29
|
return "";
|
|
28
30
|
}
|
|
29
31
|
|
|
30
|
-
const
|
|
32
|
+
const passphraseResult = zxcvbn(passphrase);
|
|
33
|
+
if (passphraseResult.score < 3) {
|
|
34
|
+
return "";
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const pinResult = zxcvbn(pin);
|
|
38
|
+
if (pinResult.score < 1) {
|
|
39
|
+
return "";
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const salt = `${passphrase.slice(-4)}${pin}`;
|
|
31
43
|
|
|
32
|
-
const passwordBuffer = Buffer.from(
|
|
44
|
+
const passwordBuffer = Buffer.from(passphrase);
|
|
33
45
|
const saltBuffer = Buffer.from(salt);
|
|
34
46
|
|
|
35
47
|
const hashBuffer = await scrypt(
|
|
@@ -87,23 +99,32 @@ const NETWORKS = [
|
|
|
87
99
|
"tron",
|
|
88
100
|
];
|
|
89
101
|
/**
|
|
90
|
-
* This function generates a transfer-link by
|
|
91
|
-
* The transfer-link
|
|
92
|
-
*
|
|
93
|
-
*
|
|
102
|
+
* This function generates a transfer-link token by encoding passphrase and PIN by Base64 and adding random padding.
|
|
103
|
+
* The transfer-link enables users to send their full ownership of a wallet account to another user for gifting or airdropping.
|
|
104
|
+
* Passphrase and PIN are validated with zxcvbn; weak values are rejected (returns null).
|
|
105
|
+
*
|
|
106
|
+
* @param {*} passphrase - Must have zxcvbn score >= 3
|
|
107
|
+
* @param {*} PIN - Must have zxcvbn score >= 1
|
|
94
108
|
* @param {*} network ethereum | polygon | arbitrum | optimism | bsc | avalanche | base | tron
|
|
95
|
-
* @returns A string formatted as a transfer-link token, which can be appended to `https://app.mybucks.online
|
|
109
|
+
* @returns A string formatted as a transfer-link token, which can be appended to `https://app.mybucks.online#wallet=`, or null if invalid/weak
|
|
96
110
|
*/
|
|
97
|
-
export function generateToken(
|
|
98
|
-
if (!
|
|
111
|
+
export function generateToken(passphrase, pin, network) {
|
|
112
|
+
if (!passphrase || !pin || !network) {
|
|
99
113
|
return null;
|
|
100
114
|
}
|
|
101
115
|
if (!NETWORKS.find((n) => n === network)) {
|
|
102
116
|
return null;
|
|
103
117
|
}
|
|
104
118
|
|
|
119
|
+
if (zxcvbn(passphrase).score < 3) {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
if (zxcvbn(pin).score < 1) {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
|
|
105
126
|
const merged = Buffer.from(
|
|
106
|
-
|
|
127
|
+
passphrase + URL_DELIMITER + pin + URL_DELIMITER + network,
|
|
107
128
|
"utf-8"
|
|
108
129
|
);
|
|
109
130
|
const base64Encoded = merged.toString("base64");
|
|
@@ -114,11 +135,11 @@ export function generateToken(password, passcode, network) {
|
|
|
114
135
|
/**
|
|
115
136
|
* This function parses a transfer-link token generated by generateToken().
|
|
116
137
|
* @param {*} token
|
|
117
|
-
* @returns an array of strings, including the
|
|
138
|
+
* @returns an array of strings, including the passphrase, pin and network.
|
|
118
139
|
*/
|
|
119
140
|
export function parseToken(token) {
|
|
120
141
|
const payload = token.slice(6, token.length - 6);
|
|
121
142
|
const base64Decoded = Buffer.from(payload, "base64").toString("utf-8");
|
|
122
|
-
const [
|
|
123
|
-
return [
|
|
143
|
+
const [passphrase, pin, network] = base64Decoded.split(URL_DELIMITER);
|
|
144
|
+
return [passphrase, pin, network];
|
|
124
145
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mybucks.online/core",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Core module of Mybucks.online Crypto Wallet",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"ethers": "^6.13.5",
|
|
39
39
|
"nanoid": "^5.0.9",
|
|
40
40
|
"scrypt-js": "^3.0.1",
|
|
41
|
-
"tronweb": "^6.0.1"
|
|
41
|
+
"tronweb": "^6.0.1",
|
|
42
|
+
"zxcvbn": "^4.4.2"
|
|
42
43
|
}
|
|
43
44
|
}
|
package/test/index.test.js
CHANGED
|
@@ -9,8 +9,8 @@ import {
|
|
|
9
9
|
parseToken,
|
|
10
10
|
} from "../index.js";
|
|
11
11
|
|
|
12
|
-
const
|
|
13
|
-
const
|
|
12
|
+
const DEMO_PASSPHRASE = "DemoAccount5&";
|
|
13
|
+
const DEMO_PIN = "112324";
|
|
14
14
|
const DEMO_NETWORK = "optimism";
|
|
15
15
|
|
|
16
16
|
const DEMO_HASH =
|
|
@@ -24,20 +24,54 @@ const DEMO_TRANSFER_TOKEN =
|
|
|
24
24
|
"VWnsSGRGVtb0FjY291bnQ1JgIxMTIzMjQCb3B0aW1pc20=_wNovT";
|
|
25
25
|
|
|
26
26
|
describe("generateHash", () => {
|
|
27
|
-
test("should return empty string if
|
|
27
|
+
test("should return empty string if passphrase or pin is blank", async () => {
|
|
28
28
|
const hash = await generateHash("", "");
|
|
29
29
|
assert.strictEqual(hash, "");
|
|
30
30
|
});
|
|
31
31
|
|
|
32
|
+
test("should return empty string for weak passphrase (zxcvbn score < 3)", async () => {
|
|
33
|
+
const weakPassphrases = [
|
|
34
|
+
"password",
|
|
35
|
+
"asdfasdf",
|
|
36
|
+
"123123123",
|
|
37
|
+
"P@ssw0rd",
|
|
38
|
+
"London123",
|
|
39
|
+
"oxford2024",
|
|
40
|
+
"John19821012",
|
|
41
|
+
"asdfASDFaSdf",
|
|
42
|
+
"qwerqwerqwer",
|
|
43
|
+
"1234567890",
|
|
44
|
+
"Julia18921012",
|
|
45
|
+
];
|
|
46
|
+
for (const passphrase of weakPassphrases) {
|
|
47
|
+
const hash = await generateHash(passphrase, DEMO_PIN);
|
|
48
|
+
assert.strictEqual(hash, "");
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("should return empty string for weak pin (zxcvbn score < 1)", async () => {
|
|
53
|
+
const weakPins = [
|
|
54
|
+
"111111",
|
|
55
|
+
"111111111111111",
|
|
56
|
+
"12341234",
|
|
57
|
+
"aaaaaaaaaa",
|
|
58
|
+
"asdfasdf",
|
|
59
|
+
];
|
|
60
|
+
for (const pin of weakPins) {
|
|
61
|
+
const hash = await generateHash(DEMO_PASSPHRASE, pin);
|
|
62
|
+
assert.strictEqual(hash, "", `Expected "" for weak pin "${pin}"`);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
32
66
|
test("should return scrypt hash result", async () => {
|
|
33
|
-
const hash = await generateHash(
|
|
67
|
+
const hash = await generateHash(DEMO_PASSPHRASE, DEMO_PIN);
|
|
34
68
|
assert.strictEqual(hash, DEMO_HASH);
|
|
35
69
|
});
|
|
36
70
|
});
|
|
37
71
|
|
|
38
72
|
describe("getEvmPrivateKey", () => {
|
|
39
73
|
test("should return 256bit private key", async () => {
|
|
40
|
-
const hash = await generateHash(
|
|
74
|
+
const hash = await generateHash(DEMO_PASSPHRASE, DEMO_PIN);
|
|
41
75
|
const privateKey = getEvmPrivateKey(hash);
|
|
42
76
|
|
|
43
77
|
assert.strictEqual(privateKey, DEMO_PRIVATE_KEY);
|
|
@@ -46,7 +80,7 @@ describe("getEvmPrivateKey", () => {
|
|
|
46
80
|
|
|
47
81
|
describe("getEvmWalletAddress", () => {
|
|
48
82
|
test("should return a valid wallet address", async () => {
|
|
49
|
-
const hash = await generateHash(
|
|
83
|
+
const hash = await generateHash(DEMO_PASSPHRASE, DEMO_PIN);
|
|
50
84
|
const address = getEvmWalletAddress(hash);
|
|
51
85
|
|
|
52
86
|
assert.strictEqual(address, DEMO_WALLET_EVM_ADDRESS);
|
|
@@ -55,7 +89,7 @@ describe("getEvmWalletAddress", () => {
|
|
|
55
89
|
|
|
56
90
|
describe("getTronWalletAddress", () => {
|
|
57
91
|
test("should return a valid wallet address", async () => {
|
|
58
|
-
const hash = await generateHash(
|
|
92
|
+
const hash = await generateHash(DEMO_PASSPHRASE, DEMO_PIN);
|
|
59
93
|
const address = getTronWalletAddress(hash);
|
|
60
94
|
|
|
61
95
|
assert.strictEqual(address, DEMO_WALLET_TRON_ADDRESS);
|
|
@@ -63,16 +97,52 @@ describe("getTronWalletAddress", () => {
|
|
|
63
97
|
});
|
|
64
98
|
|
|
65
99
|
describe("generateToken", () => {
|
|
66
|
-
test("should return null if
|
|
100
|
+
test("should return null if passphrase, pin or network is invalid", () => {
|
|
67
101
|
assert.strictEqual(generateToken("", "123345", "ethereum"), null);
|
|
68
102
|
assert.strictEqual(generateToken("", "123345"), null);
|
|
69
|
-
assert.strictEqual(generateToken("
|
|
70
|
-
assert.strictEqual(generateToken("
|
|
71
|
-
assert.strictEqual(generateToken("
|
|
103
|
+
assert.strictEqual(generateToken("passphrase", "", "ethereum"), null);
|
|
104
|
+
assert.strictEqual(generateToken("passphrase", "123456", ""), null);
|
|
105
|
+
assert.strictEqual(generateToken("passphrase", "123456", "invalid"), null);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("should return null for weak passphrase (zxcvbn score < 3)", () => {
|
|
109
|
+
const weakPassphrases = [
|
|
110
|
+
"password",
|
|
111
|
+
"asdfasdf",
|
|
112
|
+
"123123123",
|
|
113
|
+
"P@ssw0rd",
|
|
114
|
+
"London123",
|
|
115
|
+
"oxford2024",
|
|
116
|
+
"asdfASDFaSdf",
|
|
117
|
+
"qwerqwerqwer",
|
|
118
|
+
"1234567890",
|
|
119
|
+
];
|
|
120
|
+
for (const passphrase of weakPassphrases) {
|
|
121
|
+
assert.strictEqual(
|
|
122
|
+
generateToken(passphrase, DEMO_PIN, DEMO_NETWORK),
|
|
123
|
+
null
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test("should return null for weak pin (zxcvbn score < 1)", () => {
|
|
129
|
+
const weakPins = [
|
|
130
|
+
"111111",
|
|
131
|
+
"111111111111111",
|
|
132
|
+
"12341234",
|
|
133
|
+
"aaaaaaaaaa",
|
|
134
|
+
"asdfasdf",
|
|
135
|
+
];
|
|
136
|
+
for (const pin of weakPins) {
|
|
137
|
+
assert.strictEqual(
|
|
138
|
+
generateToken(DEMO_PASSPHRASE, pin, DEMO_NETWORK),
|
|
139
|
+
null
|
|
140
|
+
);
|
|
141
|
+
}
|
|
72
142
|
});
|
|
73
143
|
|
|
74
144
|
test("should return valid token", async () => {
|
|
75
|
-
const token = generateToken(
|
|
145
|
+
const token = generateToken(DEMO_PASSPHRASE, DEMO_PIN, DEMO_NETWORK);
|
|
76
146
|
|
|
77
147
|
// The first and last 6 characters serve as random padding.
|
|
78
148
|
assert.strictEqual(
|
|
@@ -83,60 +153,60 @@ describe("generateToken", () => {
|
|
|
83
153
|
|
|
84
154
|
test("should return valid token for all networks", async () => {
|
|
85
155
|
assert.notStrictEqual(
|
|
86
|
-
generateToken(
|
|
156
|
+
generateToken(DEMO_PASSPHRASE, DEMO_PIN, "ethereum"),
|
|
87
157
|
null
|
|
88
158
|
);
|
|
89
159
|
assert.notStrictEqual(
|
|
90
|
-
generateToken(
|
|
160
|
+
generateToken(DEMO_PASSPHRASE, DEMO_PIN, "polygon"),
|
|
91
161
|
null
|
|
92
162
|
);
|
|
93
163
|
assert.notStrictEqual(
|
|
94
|
-
generateToken(
|
|
164
|
+
generateToken(DEMO_PASSPHRASE, DEMO_PIN, "arbitrum"),
|
|
95
165
|
null
|
|
96
166
|
);
|
|
97
167
|
assert.notStrictEqual(
|
|
98
|
-
generateToken(
|
|
168
|
+
generateToken(DEMO_PASSPHRASE, DEMO_PIN, "optimism"),
|
|
99
169
|
null
|
|
100
170
|
);
|
|
101
171
|
assert.notStrictEqual(
|
|
102
|
-
generateToken(
|
|
172
|
+
generateToken(DEMO_PASSPHRASE, DEMO_PIN, "bsc"),
|
|
103
173
|
null
|
|
104
174
|
);
|
|
105
175
|
assert.notStrictEqual(
|
|
106
|
-
generateToken(
|
|
176
|
+
generateToken(DEMO_PASSPHRASE, DEMO_PIN, "avalanche"),
|
|
107
177
|
null
|
|
108
178
|
);
|
|
109
179
|
assert.notStrictEqual(
|
|
110
|
-
generateToken(
|
|
180
|
+
generateToken(DEMO_PASSPHRASE, DEMO_PIN, "base"),
|
|
111
181
|
null
|
|
112
182
|
);
|
|
113
183
|
assert.notStrictEqual(
|
|
114
|
-
generateToken(
|
|
184
|
+
generateToken(DEMO_PASSPHRASE, DEMO_PIN, "tron"),
|
|
115
185
|
null
|
|
116
186
|
);
|
|
117
187
|
});
|
|
118
188
|
});
|
|
119
189
|
|
|
120
190
|
describe("parseToken", () => {
|
|
121
|
-
test("should return array of
|
|
122
|
-
const [
|
|
191
|
+
test("should return array of passphrase, pin, and network", () => {
|
|
192
|
+
const [passphrase, pin, network] = parseToken(DEMO_TRANSFER_TOKEN);
|
|
123
193
|
|
|
124
|
-
assert.strictEqual(
|
|
125
|
-
assert.strictEqual(
|
|
194
|
+
assert.strictEqual(passphrase, DEMO_PASSPHRASE);
|
|
195
|
+
assert.strictEqual(pin, DEMO_PIN);
|
|
126
196
|
assert.strictEqual(network, DEMO_NETWORK);
|
|
127
197
|
});
|
|
128
198
|
});
|
|
129
199
|
|
|
130
200
|
describe("generateToken and parseToken", () => {
|
|
131
201
|
test("should be compatible and return correct result", () => {
|
|
132
|
-
const
|
|
133
|
-
const
|
|
202
|
+
const testPassphrase = "My-1st-car-was-a-red-Ford-2005!";
|
|
203
|
+
const testPin = "909011";
|
|
134
204
|
const testNetwork = "polygon";
|
|
135
|
-
const token = generateToken(
|
|
205
|
+
const token = generateToken(testPassphrase, testPin, testNetwork);
|
|
136
206
|
|
|
137
|
-
const [
|
|
138
|
-
assert.strictEqual(
|
|
139
|
-
assert.strictEqual(
|
|
207
|
+
const [passphrase, pin, network] = parseToken(token);
|
|
208
|
+
assert.strictEqual(passphrase, testPassphrase);
|
|
209
|
+
assert.strictEqual(pin, testPin);
|
|
140
210
|
assert.strictEqual(network, testNetwork);
|
|
141
211
|
});
|
|
142
212
|
});
|