Pares de llaves y billeteras

Cómo generar un nuevo Keypair (par de llaves)

Muchas de las acciones que vas a realizar con las librerías de Solana requieren un par de llaves o una billetera. Si te conectas con una billetera, no tienes que preocuparte, sin embargo, si necesitas un par de llaves, deberás de generarlas.

Press </> button to view full source
import { Keypair } from "@solana/web3.js";

(async () => {
  let keypair = Keypair.generate();
})();
from solders.keypair import Keypair

keypair = Keypair()
// clang++ generate_keypair.cpp -o generate_keypair -std=c++17 -lssl -lcrypto -lsodium

#include "solana.hpp"

using namespace many::solana;

int main() {
  auto key_pair = Keypair::generate();

  auto public_key = key_pair.public_key;
  std::cout << "public_key = " << public_key.to_base58() << std::endl;

  return 0;
}
use solana_sdk::signature::{Keypair};

fn main() {
    let wallet = Keypair::new();
}
$ solana-keygen new

# pubkey: 9ZNTfG4NyQgxy2SWjSiQoUyBPEvXT2xo7fKc5hPYYJ7b

Cómo restaurar un par de llaves a partir de una llave privada

Si ya tienes tu llave privada, puedes generar un Keypair a partir de la llave privada para probar tu dApp.

  1. Con Bytes
Press </> button to view full source
import { Keypair } from "@solana/web3.js";

(async () => {
  const keypair = Keypair.fromSecretKey(
    Uint8Array.from([
      174, 47, 154, 16, 202, 193, 206, 113, 199, 190, 53, 133, 169, 175, 31, 56,
      222, 53, 138, 189, 224, 216, 117, 173, 10, 149, 53, 45, 73, 251, 237, 246,
      15, 185, 186, 82, 177, 240, 148, 69, 241, 227, 167, 80, 141, 89, 240, 121,
      121, 35, 172, 247, 68, 251, 226, 218, 48, 63, 176, 109, 168, 89, 238, 135,
    ])
  );
})();
from solders.keypair import Keypair

secret_key= [
        174, 47, 154, 16, 202, 193, 206, 113, 199, 190, 53, 133, 169, 175, 31, 56, 222, 53, 138,
        189, 224, 216, 117, 173, 10, 149, 53, 45, 73, 251, 237, 246, 15, 185, 186, 82, 177, 240,
        148, 69, 241, 227, 167, 80, 141, 89, 240, 121, 121, 35, 172, 247, 68, 251, 226, 218, 48,
        63, 176, 109, 168, 89, 238, 135,
    ]
    
keypair = Keypair.from_bytes(secret_key)
print("Created Keypair with Public Key: {}".format(keypair.pubkey()))
// clang++ keypair_from_seed.cpp -o keypair_from_seed -std=c++17 -lssl -lcrypto -lsodium

#include "solana.hpp"

using namespace many::solana;

int main() {
  const uint8_t bytes[] = {
    174, 47, 154, 16, 202, 193, 206, 113, 199, 190, 53, 133, 169, 175, 31, 56, 222, 53, 138,
    189, 224, 216, 117, 173, 10, 149, 53, 45, 73, 251, 237, 246, 15, 185, 186, 82, 177, 240,
    148, 69, 241, 227, 167, 80, 141, 89, 240, 121, 121, 35, 172, 247, 68, 251, 226, 218, 48,
    63, 176, 109, 168, 89, 238, 135,
  };
  Keypair key_pair = Keypair::from_seed(bytes);
  std::cout << "Created Keypair with Public Key: " << key_pair.public_key.to_base58() << std::endl;
}
use solana_sdk::signature::{Keypair, Signer};

fn main() {
    let secret_key: [u8; 64] = [
        174, 47, 154, 16, 202, 193, 206, 113, 199, 190, 53, 133, 169, 175, 31, 56, 222, 53, 138,
        189, 224, 216, 117, 173, 10, 149, 53, 45, 73, 251, 237, 246, 15, 185, 186, 82, 177, 240,
        148, 69, 241, 227, 167, 80, 141, 89, 240, 121, 121, 35, 172, 247, 68, 251, 226, 218, 48,
        63, 176, 109, 168, 89, 238, 135,
    ];

    if let Ok(wallet) = Keypair::from_bytes(&secret_key) {
        let pubkey = Signer::pubkey(&wallet);
        println!("Created keypair: {}", pubkey)
    }
}
# input your secret into the Keypath listed under solana config get
  1. Con una cadena Base58
Press </> button to view full source
import { Keypair } from "@solana/web3.js";
import * as bs58 from "bs58";

(async () => {
  const keypair = Keypair.fromSecretKey(
    bs58.decode(
      "5MaiiCavjCmn9Hs1o3eznqDEhRwxo7pXiAYez7keQUviUkauRiTMD8DrESdrNjN8zd9mTmVhRvBJeg5vhyvgrAhG"
    )
  );
})();
from solders.keypair import Keypair

b58_string = "5MaiiCavjCmn9Hs1o3eznqDEhRwxo7pXiAYez7keQUviUkauRiTMD8DrESdrNjN8zd9mTmVhRvBJeg5vhyvgrAhG"
keypair = Keypair.from_string(b58_string)
print("Created Keypair with Public Key: {}".format(keypair.pubkey()))
use solana_sdk::signature::{Keypair, Signer};

fn main() {
    let wallet = Keypair::from_base58_string(
        "5MaiiCavjCmn9Hs1o3eznqDEhRwxo7pXiAYez7keQUviUkauRiTMD8DrESdrNjN8zd9mTmVhRvBJeg5vhyvgrAhG",
    );
    let pubkey = Signer::pubkey(&wallet);
    println!("Created keypair: {}", pubkey)
}

Cómo verificar un par de llaves

Si cuentas con un par de llaves, puedes verificar si la llave privada coincide con la llave pública

Press </> button to view full source
import { Keypair, PublicKey } from "@solana/web3.js";

(async () => {
  const publicKey = new PublicKey(
    "24PNhTaNtomHhoy3fTRaMhAFCRj4uHqhZEEoWrKDbR5p"
  );
  const keypair = Keypair.fromSecretKey(
    Uint8Array.from([
      174, 47, 154, 16, 202, 193, 206, 113, 199, 190, 53, 133, 169, 175, 31, 56,
      222, 53, 138, 189, 224, 216, 117, 173, 10, 149, 53, 45, 73, 251, 237, 246,
      15, 185, 186, 82, 177, 240, 148, 69, 241, 227, 167, 80, 141, 89, 240, 121,
      121, 35, 172, 247, 68, 251, 226, 218, 48, 63, 176, 109, 168, 89, 238, 135,
    ])
  );
  console.log(keypair.publicKey.toBase58() === publicKey.toBase58());
  // true
})();
from solders.keypair import Keypair
from solders.pubkey import Pubkey

public_key = Pubkey.from_string("24PNhTaNtomHhoy3fTRaMhAFCRj4uHqhZEEoWrKDbR5p")

keys = [
        174, 47, 154, 16, 202, 193, 206, 113, 199, 190, 53, 133, 169, 175, 31, 56, 222, 53, 138,
        189, 224, 216, 117, 173, 10, 149, 53, 45, 73, 251, 237, 246, 15, 185, 186, 82, 177, 240,
        148, 69, 241, 227, 167, 80, 141, 89, 240, 121, 121, 35, 172, 247, 68, 251, 226, 218, 48,
        63, 176, 109, 168, 89, 238, 135,
    ]
keypair = Keypair.from_bytes(keys)

print(keypair.pubkey() == public_key)
# True
// clang++ verify_keypair.cpp -o verify_keypair -std=c++17 -lssl -lcrypto -lsodium

#include "solana.hpp"

using namespace many::solana;

int main() {
  PublicKey public_key = PublicKey("24PNhTaNtomHhoy3fTRaMhAFCRj4uHqhZEEoWrKDbR5p");

  const uint8_t bytes[] = {
    174, 47, 154, 16, 202, 193, 206, 113, 199, 190, 53, 133, 169, 175, 31, 56, 222, 53, 138,
    189, 224, 216, 117, 173, 10, 149, 53, 45, 73, 251, 237, 246, 15, 185, 186, 82, 177, 240,
    148, 69, 241, 227, 167, 80, 141, 89, 240, 121, 121, 35, 172, 247, 68, 251, 226, 218, 48,
    63, 176, 109, 168, 89, 238, 135,
  };
  Keypair key_pair = Keypair::from_seed(bytes);

  std::cout << (public_key.to_base58() == key_pair.public_key.to_base58()) << std::endl;
}
solana-keygen verify <PUBKEY> prompt://

Cómo comprobar si una llave pública tiene una llave privada asociada

En ciertos casos especiales (por ejemplo, una dirección derivada de un programa), es posible que las claves públicas no tengan una clave privada asociada. Puedes verificar esto observando si la llave pública se encuentra en la curva ed25519. Solo las claves públicas que se encuentran en la curva pueden ser controladas por usuarios con billeteras.

Press </> button to view full source
import { PublicKey } from "@solana/web3.js";

(async function () {
  // Note that Keypair.generate() will always give a public key that is valid for users
  const key = new PublicKey("5oNDL3swdJJF1g9DzJiZ4ynHXgszjAEpUkxVYejchzrY"); // Valid public key
  console.log(PublicKey.isOnCurve(key.toBytes())); // Lies on the ed25519 curve and is suitable for users

  const offCurveAddress = new PublicKey(
    "4BJXYkfvg37zEmBbsacZjeQDpTNx91KppxFJxRqrz48e"
  ); // Valid public key
  console.log(PublicKey.isOnCurve(offCurveAddress.toBytes())); // Not on the ed25519 curve, therefore not suitable for users

  const errorPubkey = new PublicKey("testPubkey"); // Is not a valid public key
})();
from solders.pubkey import Pubkey

# Note that Keypair() will always give a public key that is valid for users
key = Pubkey.from_string('5oNDL3swdJJF1g9DzJiZ4ynHXgszjAEpUkxVYejchzrY') # Valid public key
print(key.is_on_curve()) # Lies on the ed25519 curve and is suitable for users

off_curve_address = Pubkey.from_string('4BJXYkfvg37zEmBbsacZjeQDpTNx91KppxFJxRqrz48e') # Valid public key
print(off_curve_address.is_on_curve()) # Not on the ed25519 curve, therefore not suitable for users

error_pubkey = Pubkey.from_string("testPubkey"); # Is not a valid public key
#include <iostream>
#include <solana_sdk.h>

int main()
{
    auto public_key = PublicKey("5oNDL3swdJJF1g9DzJiZ4ynHXgszjAEpUkxVYejchzrY");

    std::cout << public_key.is_on_curve() << std::endl;

    return 0;
}
use solana_sdk::pubkey::{Pubkey};
use std::str::FromStr;

fn main() {
    // Note that Keypair::new() will always give a public key that is valid for users
    let pubkey = Pubkey::from_str("5oNDL3swdJJF1g9DzJiZ4ynHXgszjAEpUkxVYejchzrY").unwrap(); // Valid public key
    println!("{:?}", pubkey.is_on_curve()); // Lies on the ed25519 curve and is suitable for users

    let off_curve_address = Pubkey::from_str("4BJXYkfvg37zEmBbsacZjeQDpTNx91KppxFJxRqrz48e").unwrap(); // Valid public key
    println!("{:?}", off_curve_address.is_on_curve()); // Not on the ed25519 curve, therefore not suitable for users

    let error_pubkey = Pubkey::from_str("testPubkey").unwrap(); // Is not a valid public key
}

Cómo generar una frase semilla (mnemonic)

Si estás creando una billetera, deberás generar una frase semilla para que el usuario pueda guardarla como respaldo.

Press </> button to view full source
import * as bip39 from "bip39";

const mnemonic = bip39.generateMnemonic();
from mnemonic import Mnemonic

mnemo = Mnemonic("english")
words = mnemo.generate(strength=256)
solana-keygen new

Cómo restaurar un par de llaves desde una frase semilla

Muchas extensiones de billetera usan frases semilla para representar sus llaves secretas. Puede convertir la frase semilla en pares de llaves para realizar pruebas locales.

  1. BIP39 - creando una wallet simple
Press </> button to view full source
import { Keypair } from "@solana/web3.js";
import * as bip39 from "bip39";

(async () => {
  const mnemonic =
    "pill tomorrow foster begin walnut borrow virtual kick shift mutual shoe scatter";
  const seed = bip39.mnemonicToSeedSync(mnemonic, ""); // (mnemonic, password)
  const keypair = Keypair.fromSeed(seed.slice(0, 32));
  console.log(`${keypair.publicKey.toBase58()}`); // 5ZWj7a1f8tWkjBESHKgrLmXshuXxqeY9SYcfbshpAqPG
})();
from solders.keypair import Keypair
from mnemonic import Mnemonic

mnemo = Mnemonic("english")
seed = mnemo.to_seed("pill tomorrow foster begin walnut borrow virtual kick shift mutual shoe scatter")
keypair = Keypair.from_bytes(seed)
print("Created Keypair with Public Key: {}".format(keypair.pubkey()))
solana-keygen recover
  1. BIP44 (varios wallets, también conocido como HD wallets)

Puede crear varias carteras a partir de una sola semilla, también conocidas como "carteras deterministas jerárquicas" o carteras HD:

Press </> button to view full source
import { Keypair } from "@solana/web3.js";
import { HDKey } from "micro-ed25519-hdkey";
import * as bip39 from "bip39";

(async () => {
  const mnemonic =
    "neither lonely flavor argue grass remind eye tag avocado spot unusual intact";
  const seed = bip39.mnemonicToSeedSync(mnemonic, ""); // (mnemonic, password)
  const hd = HDKey.fromMasterSeed(seed.toString("hex"));
  for (let i = 0; i < 10; i++) {
    const path = `m/44'/501'/${i}'/0'`;
    const keypair = Keypair.fromSeed(hd.derive(path).privateKey);
    console.log(`${path} => ${keypair.publicKey.toBase58()}`);
  }
})();
solana-keygen recover 'prompt:?key=0/0'

Cómo generar una dirección personalizada

Vanity publickeys o direcciones personalizadas son llaves que comienzan con caracteres específicos. Por ejemplo, una persona puede querer que una llave pública comience con "elv1s", o tal vez incluso "cook". Estos pueden ayudar a otras personas. Recordar a quién pertenece la llave, haciendo que la llave sea más fácilmente identificable.

Nota: Cuantos más caracteres haya en su dirección personalizada, más tiempo durará. tomar.

::: advertencia Solo debe usar la CLI para esta tarea. Los ejemplos de Python y TypeScript tienen fines ilustrativos y son mucho más lentos que la CLI. :::

Press </> button to view full source
import { Keypair } from "@solana/web3.js";

(async () => {
  let keypair = Keypair.generate();
  while (!keypair.publicKey.toBase58().startsWith("elv1s")) {
    keypair = Keypair.generate();
  }
})();
from solders.keypair import Keypair

keypair = Keypair()
while(str(keypair.pubkey())[:5]!="elv1s") :
    keypair = Keypair()
    
print("Created Keypair with Public Key: {}".format(keypair.pubkey()))
// clang++ vanity_keypair.cpp -o vanity_keypair -std=c++17 -lssl -lcrypto -lsodium

#include "solana.hpp"

using namespace many::solana;

int main() {
  auto key_pair = Keypair::generate();

  while (key_pair.public_key.to_base58().substr(0, 5) != "elv1s") {
    key_pair = Keypair::generate();
  }

  std::cout << "Created Keypair with Public Key: " << key_pair.public_key.to_base58() << std::endl;
}
solana-keygen grind --starts-with e1v1s:1

Cómo firmar y verificar mensajes con wallets

La función principal de un par de llaves es firmar mensajes y habilitar la verificación de la firma. La verificación de una firma permite el destinatario asegurarse de que los datos fueron firmados por el propietario de un clave privada específica.

Para hacerlo vamos a importar la librería crypto TweetNaClopen in new window.

Press </> button to view full source
import { Keypair } from "@solana/web3.js";
import nacl from "tweetnacl";
import { decodeUTF8 } from "tweetnacl-util";

(async () => {
  const keypair = Keypair.fromSecretKey(
    Uint8Array.from([
      174, 47, 154, 16, 202, 193, 206, 113, 199, 190, 53, 133, 169, 175, 31, 56,
      222, 53, 138, 189, 224, 216, 117, 173, 10, 149, 53, 45, 73, 251, 237, 246,
      15, 185, 186, 82, 177, 240, 148, 69, 241, 227, 167, 80, 141, 89, 240, 121,
      121, 35, 172, 247, 68, 251, 226, 218, 48, 63, 176, 109, 168, 89, 238, 135,
    ])
  );

  const message = "The quick brown fox jumps over the lazy dog";
  const messageBytes = decodeUTF8(message);

  const signature = nacl.sign.detached(messageBytes, keypair.secretKey);
  const result = nacl.sign.detached.verify(
    messageBytes,
    signature,
    keypair.publicKey.toBytes()
  );

  console.log(result);
})();
from solders.keypair import Keypair

secret_key =  [
      174, 47, 154, 16, 202, 193, 206, 113, 199, 190, 53, 133, 169, 175, 31, 56, 222, 53, 138, 189, 224, 216, 117,
      173, 10, 149, 53, 45, 73, 251, 237, 246, 15, 185, 186, 82, 177, 240, 148, 69, 241, 227, 167, 80, 141, 89, 240,
      121, 121, 35, 172, 247, 68, 251, 226, 218, 48, 63, 176, 109, 168, 89, 238, 135,
    ] 
keypair = Keypair.from_bytes(secret_key)
message = b"The quick brown fox jumps over the lazy dog"
signature = keypair.sign_message(message)
verify_sign = signature.verify(keypair.pubkey(), message)

print(verify_sign) # bool

Cómo conectar una wallet

La librereia de Solana wallet-adapteropen in new window hace más fácil el manejo de conexiones de wallets del lado cliente.

React

Ejecute el siguiente comando para instalar las dependencias requeridas:

yarn add @solana/wallet-adapter-react @solana/wallet-adapter-react-ui @solana/wallet-adapter-base @solana/wallet-adapter-wallets

Las librería wallet-adapter de React te permite persistir y acceder a los estados de conexión de una billetera a través de hooks y proveedores de contexto llamados, useWallet, WalletProvider, useConnection y ConnectionProvider. La aplicación React debe estar envuelta con WalletProvider y ConnectionProvider.

Además, podemos solicitar a los usuarios que se conecten usando useWalletModal para alternar la visibilidad del modal de conexión y también envolver la aplicación con WalletModalProvider de @solana/wallet-adapter-react-ui. El modal de conexión manejará ese flujo de conexión por nosotros, por lo que solo necesitamos escuchar cuándo se ha conectado una billetera. Sabemos que una billetera está conectada cuando la respuesta useWallet tiene una propiedad wallet no nula. Viceversa, si esa propiedad es nula, sabemos que la billetera está desconectada.

Press </> button to view full source
import React, { useMemo, FC, PropsWithChildren } from "react";
import {
  ConnectionProvider,
  WalletProvider,
} from "@solana/wallet-adapter-react";
import { WalletModalProvider } from "@solana/wallet-adapter-react-ui";
import { WalletAdapterNetwork } from "@solana/wallet-adapter-base";
import {
  LedgerWalletAdapter,
  PhantomWalletAdapter,
  SlopeWalletAdapter,
  TorusWalletAdapter,
} from "@solana/wallet-adapter-wallets";
import { clusterApiUrl } from "@solana/web3.js";
import { useWallet } from "@solana/wallet-adapter-react";
import { useWalletModal } from "@solana/wallet-adapter-react-ui";
import { MouseEventHandler } from "react";

export const Web3Provider: FC<PropsWithChildren<{}>> = ({ children }) => {
  // Can be set to 'devnet', 'testnet', or 'mainnet-beta'
  const endpoint = useMemo(
    () => clusterApiUrl(WalletAdapterNetwork.Devnet),
    []
  );

  // @solana/wallet-adapter-wallets includes all the adapters but supports tree shaking --
  // Only the wallets you configure here will be compiled into your application
  const wallets = useMemo(
    () => [
      new PhantomWalletAdapter(),
      new SlopeWalletAdapter(),
      new TorusWalletAdapter(),
      new LedgerWalletAdapter(),
    ],
    []
  );

  return (
    <ConnectionProvider endpoint={endpoint}>
      <WalletModalProvider>
        <WalletProvider wallets={wallets}>{children}</WalletProvider>
      </WalletModalProvider>
    </ConnectionProvider>
  );
};

/**
 * Make sure to wrap the App with
 * ConnectionProvider, WalletProvider, and WalletModalProvider.
 *
 * If you have a lot of Providers already, you can combine them
 * into a single wrapper (i.e. Web3Provider) instead.
 */
export const App = () => {
  return (
    <Web3Provider>
      <AppChild />
    </Web3Provider>
  );
};

const AppChild = () => {
  const { wallet } = useWallet();
  const { setVisible } = useWalletModal();

  // Display the connection modal
  const onRequestConnectWallet = () => {
    setVisible(true);
  };

  // Prompt user to connect wallet
  if (!wallet) {
    return <button onClick={onRequestConnectWallet}>Connect Wallet</button>;
  }

  return (
    <main>
      <p>Wallet successfully connected!</p>
      <p>{wallet.publicKey.toString()}</p>
    </main>
  );
};

Vue

Ejecuta el siguiente comando para instalar las dependencias requeridas:

npm install solana-wallets-vue @solana/wallet-adapter-wallets

El complemento Solana Wallets Vueopen in new window nos permite inicializar una lista de billeteras y crear una nueva propiedad global $wallet a la que se puede acceder desde cualquier componente. Todas las propiedades y métodos que puede obtener de useWallet() se muestran aquíopen in new window. También importamos y renderizamos el componente WalletMultiButton para permitir a los usuarios seleccionar una billetera y conectarse a ella.

Press </> button to view full source
<script setup>
import { WalletMultiButton } from "solana-wallets-vue";
import {
  LedgerWalletAdapter,
  PhantomWalletAdapter,
  SlopeWalletAdapter,
  TorusWalletAdapter,
} from "@solana/wallet-adapter-wallets";
import { initWallet, useWallet } from "solana-wallets-vue";

const wallets = {
  wallets: [
    new PhantomWalletAdapter(),
    new SlopeWalletAdapter(),
    new TorusWalletAdapter(),
    new LedgerWalletAdapter(),
  ],
};
initWallet(wallets);

const { connected, wallet } = useWallet();
</script>

<template>
  <div>
    <p v-if="connected">
      Wallet with public key {{ wallet.publicKey }} successfully connected!
    </p>
    <div v-else>
      <wallet-multi-button></wallet-multi-button>
    </div>
  </div>
</template>

Svelte

Ejecute el siguiente comando para instalar las dependencias requeridas:

npm install @svelte-on-solana/wallet-adapter-core @svelte-on-solana/wallet-adapter-ui @solana/wallet-adapter-base @solana/wallet-adapter-wallets @solana/web3.js

El paquete Svelte Wallet Adapteropen in new window permite agregar una tienda Svelte ($walletStore) accesible entre todos los archivos JS, TS y/o Svelte dentro de un proyecto realizado con Svelte Template o SvelteKit. Usando la referencia del repositorio aquíopen in new window puede usar el adaptador para SSR o SPA. El paquete de la interfaz de usuario contiene un componente <WalletMultiButton /> que permite a los usuarios seleccionar una billetera para conectar a ella.

Press </> button to view full source
<script>
  import { walletStore } from "@svelte-on-solana/wallet-adapter-core";
  import {
    WalletProvider,
    WalletMultiButton,
    ConnectionProvider,
  } from "@svelte-on-solana/wallet-adapter-ui";
  import { clusterApiUrl } from "@solana/web3.js";
  import {
    PhantomWalletAdapter,
    SolflareWalletAdapter,
    TorusWalletAdapter,
    LedgerWalletAdapter,
  } from "@solana/wallet-adapter-wallets";

  const localStorageKey = "walletAdapter";
  const network = clusterApiUrl("devnet"); // localhost or mainnet

  let wallets = [
    new PhantomWalletAdapter(),
    new SolflareWalletAdapter(),
    new TorusWalletAdapter(),
    new LedgerWalletAdapter(),
  ];
</script>

<WalletProvider {localStorageKey} {wallets} autoConnect />
<ConnectionProvider {network} />

{#if $walletStore?.connected} Wallet with public key {$walletStore.publicKey}
successfully connected! {:else}
<WalletMultiButton />
{/if}
Last Updated:
Contributors: Marco Ordonez, Partially Sorted