gagliardetto / solana-go

Go SDK library and RPC client for the Solana Blockchain
Apache License 2.0
723 stars 220 forks source link

Get wallets from mnemonic phrase (add BIP39 support) #86

Open Russia9 opened 1 year ago

Russia9 commented 1 year ago

Hello. How can I get solana addresses/wallets from mnemonic phrase, like in Phantom Wallet?

gagliardetto commented 1 year ago

Hey

That's something that's currently not implemented.

Adding BIP39 support is on the list of things to do.

Russia9 commented 1 year ago

I managed to do it with this code, I hope it helps somehow:

package main

import (
    "crypto/ed25519"
    "crypto/hmac"
    "crypto/sha512"
    "fmt"
    "github.com/gagliardetto/solana-go"
    "github.com/mr-tron/base58"
    "golang.org/x/crypto/pbkdf2"
    "math/big"
)

func derive(key []byte, chainCode []byte, segment uint32) ([]byte, []byte) {
    // Create buffer
    buf := []byte{0}
    buf = append(buf, key...)
    buf = append(buf, big.NewInt(int64(segment)).Bytes()...)

    // Calculate HMAC hash
    h := hmac.New(sha512.New, chainCode)
    h.Write(buf)
    I := h.Sum(nil)

    // Split result
    IL := I[:32]
    IR := I[32:]

    return IL, IR
}

const Hardened uint32 = 0x80000000

func main() {
    // BIP-39
    mnemonic := "some mnemonic"
    seed := pbkdf2.Key([]byte(mnemonic), []byte("mnemonic"), 2048, 64, sha512.New)

    // BIP-32
    h := hmac.New(sha512.New, []byte("ed25519 seed"))
    h.Write(seed)
    sum := h.Sum(nil)

    derivedSeed := sum[:32]
    chain := sum[32:]

    // BIP-44
    // m/44'/501'/index'/0'/0'
    path := []uint32{Hardened + uint32(44), Hardened + uint32(501), Hardened + uint32(0), Hardened + uint32(0)}

    for _, segment := range path {
        derivedSeed, chain = derive(derivedSeed, chain, segment)
    }

    key := ed25519.NewKeyFromSeed(derivedSeed)

    // Get Solana wallet
    wallet, err := solana.WalletFromPrivateKeyBase58(base58.Encode(key))
    if err != nil {
        panic(err)
    }

    fmt.Println(wallet.PublicKey().String())
}