WEB3DEV Español

Cover image for Cómo permitir que las carteras multisig se autentiquen con su Dapp usando ERC-1271
Juliana Cabeza
Juliana Cabeza

Posted on

Cómo permitir que las carteras multisig se autentiquen con su Dapp usando ERC-1271

EIP-1271 define una forma de verificar firmas a través de un contrato inteligente, generalmente son carteras de firmas múltiples (multisig) como Gnose Safe. Lamentablemente, algunos sitios web como OpenSea no son favorables a las carteras multisig o no desean permitir que estas carteras inicien sesión, incluso si es una opción sencilla y segura.

https://www.cryptotimes.io/gnosis-safe-is-rebranded-to-safe-with-100m-fundraise/

En esta publicación, explicaré cómo funcionan las carteras multisig, por qué son excelentes para equipos de la web3 o DAOs, y cómo puedes permitirles iniciar sesión en tu sitio de manera sencilla.

¿Cómo pueden los contratos 'firmar' los mensajes?

En algunas aplicaciones, cuando deseas conectar una cartera, es posible que debas firmar un mensaje. Este mensaje prueba que eres quien dices ser. La firma requiere una clave privada y, al ser verificada utilizando el algoritmo de recuperación, la parte que verifica podrá determinar quién firmó ese mensaje.

El problema es que una cartera multisig es un contrato inteligente, y los contratos inteligentes no tienen una clave privada y no pueden firmar mensajes. Sin embargo, gracias al EIP-1271, pueden validar un mensaje, lo que les permite delegar las firmas en una o más cuentas externas de propiedad (como las carteras comunes como Metamask).

Así es como se vería un contrato ERC1271 implementando la funcionalidad "isValidSignature" definida en el EIP-1271, que es lo más importante, pero que también requiere 3 firmas para considerar un mensaje firmado como válido. Esta es una implementación defectuosa y no probada, pero así es como podría verse:

´solidity
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.17;

import "./IERC1271.sol"; // defined in eip 1271

import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";

import "@openzeppelin/contracts/access/AccessControl.sol";

contract SimpleMultiSigWallet is IERC1271, AccessControl {

    using ECDSA for bytes32;

    // MAGICVALUE se define en eip 1271,

    // como valor de retorno para firmas válidas

    bytes4 internal constant MAGICVALUE = 0x1626ba7e;

    bytes4 internal constant INVALID_SIGNATURE = 0xffffffff;

    // lógica multisig básica, necesitamos 3 usos con la función DEFAULT_ADMIN_ROLE, para firmar un mensaje y validarlo

    uint256 minimumSignatures = 3;

    mapping(bytes32 => uint256) public messageSignatures;

    constructor() {

        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);

    }

    //solo una función para agregar una firma

    function sign(bytes32 _messageHash, bytes memory _signature) public {

        address signer = _messageHash.recover(_signature);

        if (hasRole(DEFAULT_ADMIN_ROLE, signer)) {

            messageSignatures[_messageHash] += 1;

        }

    }

    // Esto viene de eip 1271

    function isValidSignature(bytes32 _messageHash, bytes memory _signature)

        public

        view

        override

        returns (bytes4 magicValue)

    {

        address signer = _messageHash.recover(_signature);

        if (

            messageSignatures[_messageHash] >= minimumSignatures &&

            hasRole(DEFAULT_ADMIN_ROLE, signer)

        ) {

            return MAGICVALUE;

        } else {

            return INVALID_SIGNATURE;

        }

    }

}
Enter fullscreen mode Exit fullscreen mode

Cómo funciona normalmente la verificación de cartera

Veamos este diagrama que explica cómo funciona un flujo de autenticación web3 común:

  1. El servidor genera un nonce, que es un texto aleatorio, para que el usuario lo firme.
  2. El usuario firma el nonce utilizando su cartera.
  3. El servidor verifica la firma usando errcover para validar que se firmó con la cartera correcta.
  4. El servidor proporciona un token (generalmente JWT) que se utiliza para interactuar con el sitio web y los servicios.

https://www.toptal.com/ethereum/one-click-login-flows-a-metamask-tutorial

Esto es bastante simple y probablemente sea de conocimiento común para la mayoría de los desarrolladores de web3, pero está incompleto.
Es fácil entender por qué las carteras de contrato no pasan por esto, ya que no pueden firmar, por lo tanto, el back-end nunca verificará sus firmas.

¿Cómo podemos hacer que la verificación de la cartera funcione para los contratos inteligentes?

Es bastante simple, mirando el paso (5) del diagrama que podemos ver en el enlace de arriba, debemos hacer una solicitud al contrato y comprobar que la firma está aprobada por él.
Aquí tienes un ejemplo de JavaScript usando ethers:

  1. Verificamos si el firmante es una cartera o un contrato.
  2. Para los contratos, verificamos la firma llamando al contrato desde ERC1271.
  3. Para las carteras EOA (externally owned accounts), verificamos la firma de manera normal.
import { providers, utils, Contract } from "ethers";

import IERC1271Abi from "./IERC1271Abi"; // 

const MAGICVALUE = 0x1626ba7e;

// comprueba si la firma es válida

const isValidSignature = async (signingAddress, message, signature) => {

  const hash = utils.hashMessage(message);

  const provider = new providers.JsonRpcProvider(rpcUrl);

  const bytecode = await provider.getCode(address);

  const isSmartContract = bytecode && utils.hexStripZeros(bytecode) !== "0x";

  if (isSmartContract) {

    //Verifica el mensaje o la cuenta descentralizada (cartera del contrato)


    const contractWallet = new Contract(signingAddress, IERC1271Abi, provider);

    const verification = await contractWallet.isValidSignature(hash, signature);

    console.log("Message is verified?", verification === MAGICVALUE);

    return verification === MAGICVALUE;

  } else {

    // Verifica el mensaje mediante una cuenta externa propia (EOA)

    const sig = ethers.utils.splitSignature(signature);

    const recovered = await contract.verifyHash(hash, sig.v, sig.r, sig.s);

    console.log("Message is verified?", recovered === signingAddress);

    return recovered === signingAddress;

  }

};

Enter fullscreen mode Exit fullscreen mode

¿Y qué hay del aspecto de la cartera?

Afortunadamente, tenemos una solución unificada para autenticar carteras. Es gratis, simple, de código abierto y es compatible con casi cualquier cartera lista para usar, incluyendo las carteras con contrato inteligente, siempre que su sitio web admita [la conexión a una cartera])https://walletconnect.com/), ya casi ha terminado de este lado.

https://steemit.com/utopian-io/@divine-sound/walletconnect-open-protocol-for-connecting-wallets-to-dapps

Crear un cliente de WalletConnect para un contrato inteligente es bastante sencillo, y lo abordaré en una publicación futura. Sin embargo, la mayoría de las carteras de contrato tienen sus propios clientes, como Gnosis Safe.

Qué viene después

En realidad, escribí esta publicación porque quería usar Gnosis Safe, pero estoy bastante decepcionado de que OpenSea no admita contratos EIP-1271 como carteras. Como puedes ver, es bastante simple y admite una mayor descentralización y anonimato en la blockchain, al tiempo que permite que equipos y DAO colaboren en proyectos web3 de manera segura.

Gnosis intentó hacerlo realidad en 2021 en Twitter.
Hace 8 meses, hubo una publicación en la comunidad de OpenSea en Reddit al respecto, donde dieron esta respuesta genérica a algunos usuarios:

Actualmente, las carteras Multisig no son una opción ofrecida en la plataforma OpenSea. Sin embargo, estamos constantemente actualizando y agregando características para mejorar la experiencia del usuario. En este caso, enviaré estos comentarios a nuestro equipo de productos.

Este artículo fue escrito por Liron Navon y traducido por Juliana Cabeza. El original en inglés se puede encontrar aquí.

Discussion (0)