Saltearse al contenido

Adaptador de Wallet para Constructores de Dapps

Aptos proporciona un Provider y Context de React para conectar wallets de Aptos a tu dapp. Este Provider te permite especificar qué Wallets quieres permitir conexiones. Luego puedes usar el Provider para buscar información de cuenta y firmar transacciones / mensajes.

Esto proporciona una interfaz estándar para usar todas las wallets de Aptos, y permite que nuevas wallets sean fácilmente soportadas simplemente actualizando tu versión de dependencia del Adaptador de Wallet de React.

  1. Instalar @aptos-labs/wallet-adapter-react.

    Ventana de terminal
    npm install @aptos-labs/wallet-adapter-react
    Para versiones anteriores a v4.0.0

    (Opcional) Instalar los plugins para cualquier Wallet “Compatible con Estándar Legacy” que quieras soportar de esta lista.

    Sección titulada «(Opcional) Instalar los plugins para cualquier Wallet “Compatible con Estándar Legacy” que quieras soportar de esta lista.»

    Para wallets que no se han actualizado para usar el estándar AIP-62, sus plugins deben ser instalados y pasados al Provider manualmente.

    Por ejemplo:

    Ventana de terminal
    npm i @okwallet/aptos-wallet-adapter

    En App.tsx o su equivalente, importar el Adaptador de Wallet de Aptos y cualquier plugin de Wallet legacy.

    Sección titulada «En App.tsx o su equivalente, importar el Adaptador de Wallet de Aptos y cualquier plugin de Wallet legacy.»
    import { AptosWalletAdapterProvider } from "@aptos-labs/wallet-adapter-react";
    // Importar cualquier plugin de wallet adicional. Ej.
    import { OKXWallet } from "@okwallet/aptos-wallet-adapter";
    // ...
  2. Inicializar el AptosWalletAdapterProvider.

    Puedes usar cualquiera de los siguientes campos opcionales.

    Se recomienda:

    1. Establecer autoConnect a true.
    2. Establecer el dappConfig con:
    • La propiedad network establecida en la red con la que trabaja tu dapp
    • La propiedad aptosApiKeys establecida en la Api Key generada para la red especificada
    CampoDescripciónEjemplo
    autoConnectUna prop indica si la dapp debe auto conectarse con la wallet más recientemente conectada al recargar la página.true
    dappConfigEspecificar una red alternativa para trabajar. Esta prop solo funciona para wallets que NO son extensiones de chrome. Si se establece, este objeto debe incluir el nombre de la red a la que está conectada la app. El objeto puede incluir un aptosConnectDappId.{ network: 'mainnet', aptosApiKeys:{}, aptosConnectDappId: undefined }
    onErrorUna función de callback para ejecutar cuando el adaptador lanza un error.(error) => { console.log("error", error); }
    import { AptosWalletAdapterProvider } from "@aptos-labs/wallet-adapter-react";
    import { PropsWithChildren } from "react";
    import { Network } from "@aptos-labs/ts-sdk";
    export const WalletProvider = ({ children }: PropsWithChildren) => {
    return (
    <AptosWalletAdapterProvider
    autoConnect={true}
    dappConfig={{
    network: Network.MAINNET,
    aptosApiKeys: {
    mainnet: process.env.APTOS_API_KEY_MAINNET,
    }
    }}
    onError={(error) => {
    console.log("error", error);
    }}
    >
    {children}
    </AptosWalletAdapterProvider>
    );
    };
  3. Importar useWallet en archivos donde quieras acceder a datos del Provider.

    import { useWallet } from "@aptos-labs/wallet-adapter-react";
    // Access fields / functions from the adapter
    const { account, connected, wallet, changeNetwork } = useWallet();

El Repositorio del Adaptador de Wallet proporciona varios paquetes de UI para simplificar la posibilidad de que los usuarios se conecten y seleccionen una wallet.

Para componentes de UI que funcionan de forma nativa, pero son menos personalizables, elige uno de:

De lo contrario, deberías usar el selector de wallet de shadcn/ui, ya que tiene las opciones de personalización más completas. Para más detalles sobre cómo personalizar este selector de wallet o construir tu propio selector de wallet, consulta esta guía.

CampoTipoDescripción
connectedbooleanIndica si la wallet está actualmente conectada.
isLoadingbooleanIndica si una operación de wallet está actualmente cargando.
account{ address: string; publicKey: string | string[]; minKeysRequired?: number; ansName?: string | null; } | nullInformación de la cuenta actual o null si no hay ninguna cuenta conectada.
network{ name: Network; chainId?: string; url?: string; } | nullInformación de la red actual o null si no se ha seleccionado ninguna red.
wallet{ name: WalletName; icon: string; url: string; } | nullInformación de la wallet actual o null si no se ha seleccionado ninguna wallet. Incluye el nombre de la wallet, el icono y la URL.
walletsReadonlyArray<{ name: WalletName; url: string; icon: string; readyState: WalletReadyState.NotDetected; isAIP62Standard: true; }>Lista de wallets disponibles, incluyendo las estándares soportadas, cada una con nombre, URL, icono, estado de preparación, y indicación de cumplimiento con la estándar AIP-62.

Ver WalletCore.ts en wallet-adapter-core para donde se implementan estas funciones.

FunciónFirmaDescripción
connectconnect(walletName: WalletName): voidConecta a la wallet especificada por su nombre.
disconnectdisconnect(): voidDesconecta la wallet actualmente conectada.
signTransactionsignTransaction(transactionOrPayload: AnyRawTransaction | Types.TransactionPayload, asFeePayer?: boolean, options?: InputGenerateTransactionOptions): Promise<AccountAuthenticator>Firma una transacción con parámetros opcionales para el pago de tarifas.
submitTransactionsubmitTransaction(transaction: InputSubmitTransactionData): Promise<PendingTransactionResponse>Envía una transacción con los datos de transacción proporcionados.
signAndSubmitTransactionsignAndSubmitTransaction(transaction: InputTransactionData): Promise<any>Firma y envía una transacción con los datos de entrada dados.
signMessagesignMessage(message: SignMessagePayload): Promise<SignMessageResponse>Firma un mensaje y devuelve la firma y la información de respuesta.
signMessageAndVerifysignMessageAndVerify(message: SignMessagePayload): Promise<boolean>Firma un mensaje y verifica el firmante.
changeNetworkchangeNetwork(network: Network): Promise<AptosChangeNetworkOutput>Solicita un cambio en la red conectada. Esto no es soportado por todas las wallets.

Consulta el dapp de ejemplo de next.js para ver cómo se utilizan estos componentes en la práctica:

wallets es una lista de wallets disponibles, incluyendo las estándares soportadas, cada una con nombre, URL, icono, estado de preparación, y indicación de cumplimiento con la estándar AIP-62.

import { useWallet } from '@aptos-labs/wallet-adapter-react';
const displayInstalledWalletsDemo = () => {
const { wallets } = useWallet();
return (
<div>
{wallets.map(wallet => {
return <p>{wallet.name}</p>
})}
</div>
)
}

Según la estándar AIP-62, el adaptador usa un modelo de comunicación basado en eventos entre una wallet y una dapp. Esto significa que solo las wallets instaladas en el navegador del usuario son detectadas automáticamente y disponibles para su uso. Para soportar el ecosistema completo de wallets de Aptos, el adaptador mantiene un registro de wallets soportados—permitiendo que las dapps también muestren wallets no instalados. También expone una función de utilidad para gestionar fácilmente todas las wallets.

import { useWallet, groupAndSortWallets } from '@aptos-labs/wallet-adapter-react';
const displayAllWalletsDemo = () => {
const { wallets = [], notDetectedWallets = [] } = useWallet();
const { aptosConnectWallets, availableWallets, installableWallets } =
groupAndSortWallets(
[...wallets, ...notDetectedWallets]
);
return (
<div>
/** Wallets que usan el login social para crear una cuenta en la blockchain */
{aptosConnectWallets.map((aptosConnectwallet) => (
return <p>{aptosConnectwallet.name}</p>
))}
/** Wallets que están actualmente instaladas o cargables. */
{availableWallets.map((availableWallet) => (
return <p>{availableWallet.name}</p>
))}
/** Wallets que NO están actualmente instaladas o cargables. */
{installableWallets.map((installableWallet) => (
return <p>{installableWallet.name}</p>
))}
</div>
)
}

connect() establece una conexión entre la dapp y una Wallet. Luego puedes usar disconnect() para

import React from 'react';
import { WalletName, useWallet } from '@aptos-labs/wallet-adapter-react';
const WalletConnectDemo = () => {
const { connect, disconnect, account, connected } = useWallet();
const handleConnect = async () => {
try {
// Cambia a continuación el nombre de la wallet deseado en lugar de "Petra"
await connect("Petra" as WalletName<"Petra">);
console.log('Conectado a la wallet:', account);
} catch (error) {
console.error('Error al conectar a la wallet:', error);
}
};
const handleDisconnect = async () => {
try {
await disconnect();
console.log('Desconectado de la wallet');
} catch (error) {
console.error('Error al desconectar de la wallet:', error);
}
};
return (
<div>
<h1>Conexión de Wallet de Aptos</h1>
<div>
{connected ? (
<div>
<p>Conectado a: {account?.address}</p>
<button onClick={handleDisconnect}>Desconectar</button>
</div>
) : (
<button onClick={handleConnect}>Conectar Wallet</button>
)}
</div>
</div>
);
};
export default WalletConnectDemo;

Si quieres separar estos pasos, puedes usar signTransaction y submitTransaction por separado en lugar de signAndSubmitTransaction.

import React from 'react';
import { useWallet } from '@aptos-labs/wallet-adapter-react';
import { Aptos, AptosConfig, Network } from '@aptos-labs/ts-sdk';
const config = new AptosConfig({ network: Network.MAINNET });
const aptos = new Aptos(config);
const SignAndSubmit = () => {
const { account, signAndSubmitTransaction } = useWallet();
const onSignAndSubmitTransaction = async () => {
if(account == null) {
throw new Error("No se pudo encontrar la cuenta para firmar la transacción")
}
const response = await signAndSubmitTransaction({
sender: account.address,
data: {
function: "0x1::aptos_account::transfer",
functionArguments: [account.address, 1],
},
});
// si quieres esperar a la transacción
try {
await aptos.waitForTransaction({ transactionHash: response.hash });
} catch (error) {
console.error(error);
}
};
return (
<button onClick={onSignAndSubmitTransaction}>
Firmar y enviar transacción
</button>
);
};
export default SignAndSubmit;

signMessage y verifyMessage

También puedes usar el atajo signAndVerifyMessage para crear un mensaje que pueda ser verificable desde la wallet conectada.

import React, { useState } from 'react';
import { useWallet } from '@aptos-labs/wallet-adapter-react';
const SignMessageDemo = () => {
const { signMessage, signMessageAndVerify, connected, account } = useWallet();
const [message, setMessage] = useState<string>('');
const [nonce, setNonce] = useState<string>('');
const [signedMessage, setSignedMessage] = useState<any>(null);
const [verificationResult, setVerificationResult] = useState<boolean | null>(null);
const [error, setError] = useState<string | null>(null);
const handleSignMessage = async () => {
setError(null);
try {
const response = await signMessage({ message, nonce });
setSignedMessage(response);
} catch (err: any) {
setError(`Error al firmar el mensaje: ${err.message}`);
}
};
const handleVerifyMessage = async () => {
setError(null);
try {
const result = await signMessageAndVerify({ message, nonce });
setVerificationResult(result);
} catch (err: any) {
setError(`Error al verificar el mensaje: ${err.message}`);
}
};
return (
<div>
<h1>Firma y Verifica Mensaje de Aptos</h1>
<div>
{connected ? (
<div>
<p>Conectado a: {account?.address}</p>
<div className="flex flex-col gap-4">
<textarea
value={message}
onChange={(e) => setMessage(e.target.value)}
placeholder="Introduce tu mensaje aquí"
className="border rounded p-2"
/>
<input
type="text"
value={nonce}
onChange={(e) => setNonce(e.target.value)}
placeholder="Introduce el nonce (cadena aleatoria) aquí"
className="border rounded p-2 mt-2"
/>
<button onClick={handleSignMessage} className="bg-blue-500 text-white rounded p-2 mt-2">
Firmar Mensaje
</button>
{signedMessage && (
<div>
<h4>Mensaje Firmado</h4>
<pre>{JSON.stringify(signedMessage, null, 2)}</pre>
<button onClick={handleVerifyMessage} className="bg-green-500 text-white rounded p-2 mt-2">
Verificar Mensaje
</button>
</div>
)}
{verificationResult !== null && (
<div>
<h4>Resultado de Verificación</h4>
<p>{verificationResult ? 'El mensaje está verificado!' : 'No se pudo verificar el mensaje.'}</p>
</div>
)}
{error && (
<div className="text-red-600">
<p>{error}</p>
</div>
)}
</div>
</div>
) : (
<p>Por favor, conecta tu wallet para firmar y verificar mensajes.</p>
)}
</div>
</div>
);
};
export default SignMessageDemo;

changeNetwork (No soportado por todas las wallets)

Sección titulada «changeNetwork (No soportado por todas las wallets)»

Algunas wallets solo soportan mainnet, por lo que no soportarán changeNetwork. Si dependes de esta característica, asegúrate de implementar el manejo de errores para si una wallet que no soporta changeNetwork. Nightly es un ejemplo de una wallet que soporta changeNetwork.

import React from 'react';
import { useWallet } from '@aptos-labs/wallet-adapter-react';
import { Network } from '@aptos-labs/ts-sdk';
const ChangeNetworkDemo = () => {
const { network, changeNetwork, wallet } = useWallet();
const isNetworkChangeSupported = wallet?.name === "Nightly";
const isValidNetworkName = () => {
return network && Object.values<string>(Network).includes(network.name);
};
return (
<div>
<h4>Información de Red</h4>
<div>
<div><strong>Nombre de Red</strong></div>
<div>
<span style={{ color: isValidNetworkName() ? 'green' : 'red' }}>
{network?.name ?? 'No Presente'}
</span>
{` (Esperado: ${Object.values<string>(Network).join(', ')})`}
</div>
<div><strong>URL</strong></div>
<div>
{network?.url ? (
<a href={network.url} target="_blank" rel="noreferrer">
{network.url}
</a>
) : (
'No Presente'
)}
</div>
<div><strong>ID de Cadena</strong></div>
<div>{network?.chainId ?? 'No Presente'}</div>
</div>
<div>
<h4>Cambiar Red</h4>
<div>
<label>
<input
type="radio"
name="network"
value={Network.DEVNET}
checked={network?.name === Network.DEVNET}
onChange={() => changeNetwork(Network.DEVNET)}
disabled={!isNetworkChangeSupported}
/>
Devnet
</label>
<label>
<input
type="radio"
name="network"
value={Network.TESTNET}
checked={network?.name === Network.TESTNET}
onChange={() => changeNetwork(Network.TESTNET)}
disabled={!isNetworkChangeSupported}
/>
Testnet
</label>
<label>
<input
type="radio"
name="network"
value={Network.MAINNET}
checked={network?.name === Network.MAINNET}
onChange={() => changeNetwork(Network.MAINNET)}
disabled={!isNetworkChangeSupported}
/>
Mainnet
</label>
</div>
{!isNetworkChangeSupported && (
<div>
* {wallet?.name ?? 'Esta wallet'} no soporta solicitudes de cambio de red
</div>
)}
</div>
</div>
);
};
export default ChangeNetworkDemo;

signAndSubmitBCSTransaction(payload) (No soportado por todas las wallets)

Sección titulada «signAndSubmitBCSTransaction(payload) (No soportado por todas las wallets)»

Es similar a la lógica de signAndSubmit, pero usa un formato BCS para el data de la transacción.

const onSignAndSubmitBCSTransaction = async () => {
const response = await signAndSubmitTransaction({
sender: account.address,
data: {
function: "0x1::aptos_account::transfer",
functionArguments: [AccountAddress.from(account.address), new U64(1)],
},
});
// si quieres esperar a la transacción
try {
await aptos.waitForTransaction({ transactionHash: response.hash });
} catch (error) {
console.error(error);
}
};
<button onClick={onSignAndSubmitTransaction}>
Firmar y enviar transacción BCS
</button>;

Dado que las extensiones de Chrome no son soportadas por defecto en los navegadores móviles, el adaptador mantiene un registry de wallets no detectadas, incluyendo una propiedad deeplinkProvider para wallets que soportan enlaces profundos. Esto permite que la dapp muestre wallets que no son detectables en una vista de navegador móvil pero que aún pueden ser conectadas redirigiendo al usuario a una vista de navegador de aplicación en el navegador.

{
name: "Petra",
url: "https://chromewebstore.google.com/detail/petra-aptos-wallet/ejjladinnckdgjemekebdpeokbikhfci?hl=en",
icon: "data:image/png;base64,iVBOR...QmCC",
readyState: WalletReadyState.NotDetected,
isAIP62Standard: true,
deeplinkProvider: "https://petra.app/explore?link=",
}

Para renderizar wallets con soporte para deeplinkProvider en tu dapp—asumiendo que no estás usando el selector de wallet oficial del adaptador—sigue estos pasos:

  1. Recupera todas las wallets compatibles y agrupalas por tipo de wallet

    import { useWallet, groupAndSortWallets } from '@aptos-labs/wallet-adapter-react';
    const displayAllWalletsDemo = () => {
    const { wallets = [], notDetectedWallets = [] } = useWallet();
    const { aptosConnectWallets, availableWallets, installableWallets } =
    groupAndSortWallets(
    [...wallets, ...notDetectedWallets]
    );
    return (
    <div>
    /** Wallets que usan el login social para crear una cuenta en la blockchain */
    {aptosConnectWallets.map((aptosConnectwallet) => (
    <WalletItemComponent wallet={aptosConnectwallet}/>
    ))}
    /** Wallets que están actualmente instaladas o cargables. */
    {availableWallets.map((availableWallet) => (
    <WalletItemComponent wallet={availableWallets}/>
    ))}
    /** Wallets que NO están actualmente instaladas o cargables. */
    {installableWallets.map((installableWallet) => (
    <WalletItemComponent wallet={installableWallets}/>
    ))}
    </div>
    )
    }

    Este fragmento de código recupera todas las wallets en el ecosistema de Aptos que son soportadas por el adaptador de wallet.

  2. Muestra las wallets no instaladas con soporte para enlaces profundos en la vista móvil.

    Para asegurarnos de que solo se muestren wallets que soportan enlaces profundos en móviles, podemos verificar tanto el soporte para deepLinkProvider como el tipo de vista actual.

    En el componente que renderiza cada wallet:

    import { useWallet, WalletReadyState } from '@aptos-labs/wallet-adapter-react';
    const WalletItemComponent = (wallet) => {
    const { connect } = useWallet();
    // En móviles, las wallets de extensión nunca tendrán un estado de `Installed`
    const isWalletReady = wallet.readyState === WalletReadyState.Installed;
    // Verifica si la wallet soporta enlaces profundos en móviles.
    const mobileSupport =
    "deeplinkProvider" in wallet && wallet.deeplinkProvider;
    // Si la wallet no está instalada, el usuario está en una vista redirigible (es decir, navegador móvil pero no navegador de aplicación),
    // y la wallet no soporta enlaces profundos—no mostrar la wallet.
    if (!isWalletReady && isRedirectable() && !mobileSupport) return null;
    // De lo contrario, mostrar la wallet
    return (
    <Button onClick={connect(wallet)}>{wallet.name}</Button>
    )
    }

    Este fragmento de código asegura que el objeto wallet correcto se muestre en la vista apropiada.