1. Clona el repositorio

    // https
    git clone <https://github.com/angelmc32/ledger-integration-tutorial.git>
    
    // ssh
    git clone [email protected]:angelmc32/ledger-integration-tutorial.git
    
  2. Instala dependencias

    // yarn
    yarn install
    
    // npm
    npm install
    

    Las dependencias son las siguientes:

    "dependencies": {
    // Necesario para habilitar Buffer en la app de React
        "@esbuild-plugins/node-globals-polyfill": "^0.2.3",
    // Paqueterías para integrar Ledger
        "@ledgerhq/hw-app-eth": "^6.32.1",
        "@ledgerhq/hw-transport-webhid": "^6.27.12",
        "@ledgerhq/hw-transport-webusb": "^6.27.12",
    // Necesario para conectar Ledger en la app de React
        "buffer": "^6.0.3",
    // Nos va a facilitar la comunicación con EVM
        "ethers": "5.5.4",
    // React
        "react": "^18.2.0",
        "react-dom": "^18.2.0",
    // Toast para mejor UX
        "react-hot-toast": "^2.4.0"
      },
      "devDependencies": {
    // React
        "@types/react": "^18.0.28",
        "@types/react-dom": "^18.0.11",
    // Plugin de Compilador SWC
        "@vitejs/plugin-react-swc": "^3.0.0",
    // Tailwind
        "autoprefixer": "^10.4.14",
        "postcss": "^8.4.21",
        "prettier": "^2.8.6",
        "prettier-plugin-tailwindcss": "^0.2.5",
        "tailwindcss": "^3.2.7",
    // Vite
        "vite": "^4.2.0"
      },
      "alias": {
        "@ledgerhq/devices": "@ledgerhq/devices/lib-es"
      }
    
  3. Agrega un archivo .env en directorio raíz, y agrega la siguiente variable de ambiente

VITE_ALCHEMY_API_URL=https://eth-sepolia.g.alchemy.com/v2/DdYx5wS15WDafXvSOQ8BDFc85TdOaT9e
  1. Instanciamos nuestro Proveedor para comunicación con la EVM, y las variables de estado que utilizaremos en la aplicación
// 

const ALCHEMY_RPC_URL = import.meta.env.VITE_ALCHEMY_API_URL;
const provider = new ethers.providers.JsonRpcProvider(ALCHEMY_RPC_URL);

const [addressState, setAddressState] = useState(null);
const [ethState, setEthState] = useState(null);
const [recipientState, setRecipientState] = useState(null);
const [showModal, setShowModal] = useState(false);
const [transferTxState, setTransferTxState] = useState({
  to: null,
  gasPrice: null,
  gasLimit: null,
  nonce: null,
  chainId: 11155111,
  data: null,
  value: null,
});
const [isLoading, setIsLoading] = useState(false);
const [url, setUrl] = useState();
  1. Exploramos los componentes del frontend:

El formulario nos va a servir para definir la información de la transacción, que será enviada al Ledger para firmar, y posteriormente enviada a la EVM a través de nuestro Proveedor

Hay 2 botones que utilizaremos, a) Para abrir el modal donde se ejecuta la lógica para conectar nuestro Ledger a la aplicación. b) Para solicitar la firma de la transacción y su envío a la EVM

ConnectLedgerModal es el modal donde inicializamos nuestras variables de addressState y también ethState.

Cuando el usuario seleccione Ledger como cartera (única opción), se ejecutará una función asíncrona, que definiremos como connectLedger.

El primer paso es instanciar nuestro método de transporte entre la aplicación y el Ledger físico. Utilizaremos el método WebHID (Human Interface Device)

Con el transporte, utilizaremos la paquetería creada por Ledger para comunicarnos con la aplicación de Ethereum que está hospedada dentro del Ledger del usuario. Instanciamos el objeto Eth como eth, y lo guardamos dentro de nuestra variable ethState.

```jsx
const connectLedger = async () => {
	const transport = await TransportWebHID.create();
	const eth = new Eth(transport);
	const { address } = await eth.getAddress("44'/60'/0'/0/0", false);
	setAddressState(address);
	setEthState(eth);

	// Por hacer: calcular el precio de gas y el límite de gas
}
```

Obtenemos el precio del gas, y definimos el límite de gas a usar en la transacción. 

Precaución: Esto puede impactar el precio final de la transacción, aquí no realizamos ninguna validación y establecemos un límite de gas alto, ya que es un tutorial. Para aplicaciones en producción, se sugiere investigar y crear una mejor lógica para calcular el límite de gas.

Guardamos los valores del gas en la variable de estado transferTxState.

```jsx
const connectLedger = async () => {
	// Código anterior...

	let gasPriceCalc = (
		await provider.getGasPrice())._hex;
		gasPriceCalc = parseInt(parseInt(gasPriceCalc, 16) * 1.15);
		setTransferTxState((prevState) => ({
		  ...prevState,
		  gasLimit: 32000,
		  gasPrice: gasPriceCalc,
		});
	);
}
```

Queremos darle feedback a nuestros usuarios, por lo que agregamos el toast. Para manejar errores, colocamos la lógica dentro de un bloque try-catch. Así quedaría nuestra función connectLedger:

```jsx
const connectLedger = async () => {
  try {
    const transport = await TransportWebHID.create();
    const eth = new Eth(transport);
    const { address } = await eth.getAddress("44'/60'/0'/0/0", false);
    setAddressState(address);
    setEthState(eth);
    let gasPriceCalc = (await provider.getGasPrice())._hex;
    gasPriceCalc = parseInt(parseInt(gasPriceCalc, 16) * 1.15);
    setTransferTxState((prevState) => ({
      ...prevState,
      gasLimit: 32000,
      gasPrice: gasPriceCalc,
    }));
    toast.success("Ledger conectado");
    setShowModal(false);
  } catch (error) {
    toast.error("Ocurrió un error, intenta de nuevo");
    setShowModal(false);
  }
};
```

Al conectar el Ledger, cargamos la información guardada en nuestras variables de estado en nuestro formulario, y estamos listos para crear una transacción

El usuario va a poder llenar la dirección donde recibirá el ETH, así como la cantidad a transferir (en ETH).
  1. Para enviar la transacción, necesitamos solicitarle al Ledger que firme la transacción de tal manera que se utilicen sus fondos para ejecutarla.

Bloqueamos el botón para evitar doble ejecución, y obtenemos el Nonce actual de la cartera de nuestro usuario. Para ello, utilizamos la variable de estado addressState, que contiene la llave pública o dirección de nuestro usuario.

Ahora podemos definir el objeto que contendrá los valores de la transacción, y la serializamos de tal manera que queda lista para ser firmada por el Ledger del usuario:

```jsx
const transactionTransfer = async () => {
	setIsLoading(true);
  const nonce = await provider.getTransactionCount(addressState, "latest");

	const transaction = {
	  to: recipientState,
	  gasPrice: transferTxState.gasPrice,
	  gasLimit: ethers.utils.hexlify(32000),
	  nonce: nonce,
	  chainId: transferTxState.chainId,
	  data: "0x00",
	  value: ethers.utils.parseUnits(transferTxState.value, "ether")._hex,
	};
	
	let unsignedTx = ethers.utils.serializeTransaction(transaction).slice(2);

	// No terminado, en proceso...
}
```
  1. Solicitamos la firma del usuario a través de la aplicación de Ethereum instalada en el Ledger, modificamos algunos valores para que sean compatibles con nuestro Proveedor y volvemos a serializar , enviamos la transacción a la EVM

    
    const transactionTransfer = async () => {
    	
    	// Código anterior...
    	
    	const signature = await ethState.signTransaction(
    	  "44'/60'/0'/0/0",
    	  unsignedTx,
    	  null
    	);
    	
    	// Modificamos las firmas para preparar nuestra firma para nuestro Proveedor
    	signature.r = "0x" + signature.r;
    	signature.s = "0x" + signature.s;
    	signature.v = parseInt("0x" + signature.v);
    	signature.from = addressState;
    	
    	//Serializar de nuevo, esta vez incluyendo la firma
    	let signedTx = ethers.utils.serializeTransaction(transaction, signature);
    	
    	// Enviamos la firma a la EVM
    	const hash = (await provider.sendTransaction(signedTx)).hash;
    	
    	if (hash) {
    	  toast.success("Se ha creado tu tx exitosamente");
    	  setUrl("<https://sepolia.etherscan.io/tx/>" + hash);
    	  setIsLoading(false);
    }
    

    De nuevo, colocaremos nuestra interacción asíncrona con el Ledger dentro de un bloque try-catch para darle una mejor UX a nuestra aplicación

    Así quedaría nuestra función transactionTransfer:

    const transactionTransfer = async () => {
      setIsLoading(true);
      const nonce = await provider.getTransactionCount(addressState, "latest");
      const transaction = {
        to: recipientState,
        gasPrice: transferTxState.gasPrice,
        gasLimit: ethers.utils.hexlify(32000),
        nonce: nonce,
        chainId: transferTxState.chainId,
        data: "0x00",
        value: ethers.utils.parseUnits(transferTxState.value, "ether")._hex,
      };
      //Serializing the transaction to pass it to Ledger Nano for signing
      let unsignedTx = ethers.utils.serializeTransaction(transaction).slice(2);
    
      try {
        //Sign with the Ledger Nano (Sign what you see)
        const signature = await ethState.signTransaction(
          "44'/60'/0'/0/0",
          unsignedTx,
          null
        );
    
        //Parse the signature
        signature.r = "0x" + signature.r;
        signature.s = "0x" + signature.s;
        signature.v = parseInt("0x" + signature.v);
        signature.from = addressState;
    
        //Serialize the same transaction as before, but adding the signature on it
        let signedTx = ethers.utils.serializeTransaction(transaction, signature);
    
        //Sending the transaction to the blockchain
        const hash = (await provider.sendTransaction(signedTx)).hash;
    
        if (hash) {
          toast.success("Se ha creado tu tx exitosamente");
          setUrl("<https://sepolia.etherscan.io/tx/>" + hash);
          setIsLoading(false);
        }
      } catch (error) {
        toast.error("Ocurrió un error, intenta de nuevo");
        setIsLoading(false);
      }
    };
    
  2. Ahora podemos probar nuestra aplicación, intentemos mandar una transacción. El flujo es el siguiente:

    1. Conecta tu Ledger a tu computadora vía USB
    2. Desbloquea tu Ledger
    3. Abre la aplicación de Ethereum en tu Ledger
    4. Haz click en el botón de “Conecta tu Ledger”
    5. Introduce la dirección a donde vas a enviar el ETH
    6. Introduce el valor que vas a transferir
    7. Haz click en “Crear Transacción”
    8. Firma la transacción en tu Ledger
    9. Si todo sale bien, podrás ver la transacción en la aplicación