Skip to content

Commit 2e698ce

Browse files
committed
feat: create solana wallet; recover wallet from 12-words in url
1 parent b1d4912 commit 2e698ce

File tree

4 files changed

+362
-46
lines changed

4 files changed

+362
-46
lines changed

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
"version": "2.2",
44
"private": true,
55
"dependencies": {
6+
"@noble/ed25519": "^2.2.3",
7+
"@noble/hashes": "^1.7.1",
8+
"@solana/web3.js": "^1.98.0",
69
"@testing-library/jest-dom": "^5.17.0",
710
"@testing-library/react": "^13.4.0",
811
"@testing-library/user-event": "^13.5.0",
@@ -11,6 +14,7 @@
1114
"@types/react-dom": "^18.3.1",
1215
"async": "^3.2.6",
1316
"axios": "^1.7.7",
17+
"bip39": "^3.1.0",
1418
"blobshape": "^1.0.0",
1519
"copy-to-clipboard": "^3.3.3",
1620
"ethers": "^6.13.4",
@@ -56,4 +60,4 @@
5660
"@types/blobshape": "^1.0.3",
5761
"@types/node": "^22.9.3"
5862
}
59-
}
63+
}

src/services/wallets.ts

Lines changed: 65 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,44 @@ import {
88
import {
99
apiv4_endpoint,
1010
conetProvider,
11-
conetRpc,
1211
localDatabaseName,
1312
} from "../utils/constants";
1413
import contracts from "../utils/contracts";
1514
import { CoNET_Data, setCoNET_Data } from "../utils/globals";
15+
import { Keypair } from "@solana/web3.js";
16+
import { mnemonicToSeedSync } from "bip39";
17+
import { sha512 } from "@noble/hashes/sha512";
1618

1719
const PouchDB = require("pouchdb").default;
1820

1921
let isGetFaucetProcess = false;
2022

2123
let getFaucetRoop = 0;
2224

25+
// Function to derive the keypair from mnemonic
26+
async function getSolanaKeypairFromMnemonic(mnemonic: string) {
27+
// Convert mnemonic to seed
28+
const seed = mnemonicToSeedSync(mnemonic);
29+
30+
// Derive the seed for the first account using Solana's HD wallet path
31+
const derivedSeed = await deriveSolanaSeed(seed);
32+
33+
// Generate a Keypair from the derived seed
34+
return Keypair.fromSeed(derivedSeed);
35+
}
36+
37+
// Function to derive the seed for the given derivation path
38+
async function deriveSolanaSeed(seed: any) {
39+
// Derive a 32-byte key from the seed using SHA512 (Solana's derivation process)
40+
const hash = sha512(seed);
41+
return hash.slice(0, 32); // Take the first 32 bytes as the private key
42+
}
43+
2344
const createOrGetWallet = async (secretPhrase: string | null) => {
2445
await checkStorage();
2546

47+
if (secretPhrase) setCoNET_Data(null);
48+
2649
if (!CoNET_Data || !CoNET_Data?.profiles) {
2750
const acc = createKeyHDWallets(secretPhrase);
2851

@@ -44,6 +67,7 @@ const createOrGetWallet = async (secretPhrase: string | null) => {
4467
privateKeyArmor: acc.signingKey.privateKey,
4568
hdPath: acc.path,
4669
index: acc.index,
70+
type: "ethereum",
4771
};
4872

4973
const data: any = {
@@ -54,55 +78,64 @@ const createOrGetWallet = async (secretPhrase: string | null) => {
5478
nonce: 0,
5579
};
5680

57-
const primaryWallet = ethers.Wallet.fromPhrase(data.mnemonicPhrase);
58-
const secondaryWallet = primaryWallet.deriveChild(0);
59-
60-
const profile2: profile = {
61-
tokens: initProfileTokens(),
62-
publicKeyArmor: secondaryWallet.publicKey,
63-
keyID: secondaryWallet.address,
64-
isPrimary: true,
65-
referrer: null,
66-
isNode: false,
67-
pgpKey: {
68-
privateKeyArmor: key.privateKey,
69-
publicKeyArmor: key.publicKey,
70-
},
71-
privateKeyArmor: secondaryWallet.signingKey.privateKey,
72-
hdPath: secondaryWallet.path,
73-
index: secondaryWallet.index,
74-
};
75-
76-
data.profiles.push(profile2);
81+
if (acc?.mnemonic?.phrase) {
82+
const secondaryWallet: Keypair = await getSolanaKeypairFromMnemonic(
83+
acc?.mnemonic?.phrase
84+
);
85+
86+
const profile2: profile = {
87+
tokens: initProfileTokens(),
88+
publicKeyArmor: secondaryWallet.publicKey.toString(),
89+
keyID: secondaryWallet.publicKey.toBase58(),
90+
isPrimary: true,
91+
referrer: null,
92+
isNode: false,
93+
pgpKey: {
94+
privateKeyArmor: key.privateKey,
95+
publicKeyArmor: key.publicKey,
96+
},
97+
privateKeyArmor: secondaryWallet.secretKey.toString(),
98+
hdPath: null,
99+
index: 0,
100+
type: "solana",
101+
};
102+
103+
data.profiles.push(profile2);
104+
}
77105

78106
setCoNET_Data(data);
79107
}
80108

81109
const tmpData = CoNET_Data;
82-
83-
if (tmpData && tmpData?.profiles.length < 2) {
84-
const primaryWallet = ethers.Wallet.fromPhrase(tmpData.mnemonicPhrase);
85-
const secondaryWallet = primaryWallet.deriveChild(0);
86-
110+
if (tmpData) tmpData.profiles.length = 2;
111+
112+
if (
113+
tmpData &&
114+
(tmpData?.profiles.length < 2 || tmpData?.profiles[1]?.type !== "solana")
115+
) {
116+
const secondaryWallet = await getSolanaKeypairFromMnemonic(
117+
tmpData.mnemonicPhrase
118+
);
87119
const key = await createGPGKey("", "", "");
88120

89121
const profile2: profile = {
90122
tokens: initProfileTokens(),
91-
publicKeyArmor: secondaryWallet.publicKey,
92-
keyID: secondaryWallet.address,
123+
publicKeyArmor: secondaryWallet.publicKey.toString(),
124+
keyID: secondaryWallet.publicKey.toBase58(),
93125
isPrimary: true,
94126
referrer: null,
95127
isNode: false,
96128
pgpKey: {
97129
privateKeyArmor: key.privateKey,
98130
publicKeyArmor: key.publicKey,
99131
},
100-
privateKeyArmor: secondaryWallet.signingKey.privateKey,
101-
hdPath: secondaryWallet.path,
102-
index: secondaryWallet.index,
132+
privateKeyArmor: secondaryWallet.secretKey.toString(),
133+
hdPath: null,
134+
index: 0,
135+
type: "solana",
103136
};
104137

105-
tmpData.profiles.push(profile2);
138+
tmpData.profiles[1] = profile2;
106139
}
107140

108141
tmpData?.profiles.forEach(async (n: profile) => {

src/types.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ interface freePassport {
4848
expiresDays: string;
4949
}
5050

51+
type keyPairType = "ethereum" | "solana";
52+
5153
interface profile extends keyPair {
5254
isPrimary?: boolean;
5355
pgpKey?: pgpKeyPair;
@@ -59,6 +61,7 @@ interface profile extends keyPair {
5961
isNode: boolean;
6062
referrer: string | null | undefined;
6163
data?: any;
64+
type?: keyPairType;
6265
nodeID?: number;
6366
nodeIP_address?: string;
6467
nodeRegion?: string;

0 commit comments

Comments
 (0)