3

Steven and John answered on how to generate and save a keypair using web3.js v2.
-> see also: How to generate and save a keypair with solana/web3.js v2?

I formatted the private key into base58 (server-side) for importing into Phantom wallet, see below.
However, importing the output, 'GxF5ePs9h98vsW5EpTy8Jf8Fgtg9s1BiAUH1jboXX4td', into Phantom gives an error "incorrect format". Any idea why and how to resolve?

export const generateKeypair = async (): Promise<{ publicKey: string; privateKey: string }> => {
    // Generate a new Ed25519 keypair
    const keypair = await crypto.subtle.generateKey(
        "Ed25519",
        true,
        ["sign", "verify"]
    );

    const publicKey = await getAddressFromPublicKey(keypair.publicKey)

    // Export the private key in JWK format
    const privateKeyJwk = await crypto.subtle.exportKey("jwk", keypair.privateKey);
    console.log(privateKeyJwk)

    // Get the 'd' (private key) from the JWK
    const privateKeyBase64Url = privateKeyJwk.d; ;
    if (!privateKeyBase64Url) throw new Error("Failed to get private key bytes");

    // Decode Base64 to raw bytes (Uint8Array)
    const privateKeyBase64 = privateKeyBase64Url.replace(/-/g, '+').replace(/_/g, '/');  
    const privateKeyBytes = new Uint8Array(atob(privateKeyBase64).split('').map(c => c.charCodeAt(0)));
    console.log(privateKeyBytes)

    // Encode the raw bytes in Base58
    const privateKey = bs58.encode(privateKeyBytes);

    console.log({ publicKey, privateKey })
    return { publicKey, privateKey };
};


// OUTPUT
//   {
//     key_ops: [ 'sign' ],
//     ext: true,
//     crv: 'Ed25519',
//     d: '7QYrNgpIfW5cEXQXE2jjGm-_kIwR40nfi2pVA49dn-4',
//     x: 'Vnxjs2cyrXY9lTYkpqgZh8M5TkUzjbiX4UISNGrnHec',
//     kty: 'OKP'
//   }
//   Uint8Array(32) [
//     237,   6,  43,  54,  10,  72, 125, 110,
//      92,  17, 116,  23,  19, 104, 227,  26,
//     111, 191, 144, 140,  17, 227,  73, 223,
//     139, 106,  85,   3, 143,  93, 159, 238
//   ]
//   {
//     publicKey: '6pc5DUQBRmsegskr7DEZfs3wcLAaSPCwaKkXyEhkRZjt',
//     privateKey: 'GxF5ePs9h98vsW5EpTy8Jf8Fgtg9s1BiAUH1jboXX4td'
//   }

1 Answer 1

3

This Phantom help article gives you an indication of what format they expect the key to be in: (link)

Solana private key👇 2P3qgS5A18gGmZJmYHNxYrDYPyfm6S3dJgs8tPW6ki6i2o4y9293r5N8CF7JpEtQiW8mx1kSktpgyDG1xuWNzfsM

That is 64 bytes of data, encoded as a base58 string. This is a typical format for secret keys, consisting of the 32 bytes of the private key, followed by the 32 bytes of its corresponding public key.

Start with your keypair

const keypair = await crypto.subtle.generateKey(
    'Ed25519',
    true, /* extractable */
    ["sign", "verify"]
);

Export the public key bytes

const publicKeyBytes = crypto.subtle.exportKey('raw', keypair.publicKey);

Export the private key bytes

const privateKeyBytes = (await crypto.subtle.exportKey(
  "pkcs8",
  keypair.privateKey
)).slice(-32);

Glue them together and encode as a base58 string

const secretKeyBytes = new Uint8Array([
  ...new Uint8Array(privateKeyBytes),
  ...new Uint8Array(publicKeyBytes),
]);
const encodedSecretKey = getBase58Decoder().decode(secretKeyBytes);
2
  • As always, be extremely careful with private key material. Knowledge of the private key bytes is sufficient to take complete control of the assets associated with it. Commented Nov 22 at 19:47
  • 1
    Thank you! It works, I did not know that the wallets require 64 bytes. Learned that secretKey is private + public key.
    – jonasXchen
    Commented Nov 23 at 1:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.