WEB3DEV

Cover image for Como permitir que carteiras multisig se autentiquem com seu Dapp usando o ERC-1271.
Adriano P. Araujo
Adriano P. Araujo

Posted on

Como permitir que carteiras multisig se autentiquem com seu Dapp usando o ERC-1271.

EIP-1271 define uma maneira de verificar assinaturas por meio de um contrato inteligente, geralmente são carteiras de assinatura múltipla ( multisig ), como Gnose Safe, infelizmente, alguns sites, como OpenSea, não gostam de carteiras multisig ou não querem permitir que carteiras multisig efetuem login, mesmo que seja simples e seguro.

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

Portanto, neste post, explicarei como as carteiras multisig funcionam, por que elas são ótimas para equipes da web3 ou de DAOs e como você pode permitir que elas façam login no seu site facilmente.

Como os Contratos podem ‘assinar’ mensagens?

Em alguns aplicativos, quando você deseja conectar uma carteira, pode ser necessário assinar uma mensagem, essa mensagem prova que você é quem você diz ser, a assinatura requer uma chave privada e, quando marcada usando o algoritmo de recuperação, a parte verificadora saberia quem assinou essa mensagem.

O problema é que uma carteira multisig é um contrato inteligente, contratos inteligentes não têm chave privada e não podem assinar mensagens, no entanto, graças ao EIP-1271, eles podem validar uma mensagem, o que lhes permite delegar as assinaturas em uma ou mais contas de propriedade externa (carteiras comuns como Metamask).

É assim que um contrato ERC1271 seria, implementando a funcionalidade “isValidSignature” definida em EIP-1271, que é a coisa mais importante, mas que também requer 3 assinaturas para tratar uma mensagem assinada como válida, puramente como uma implementação, essa é uma implementação falha e não testada, mas é assim que pode parecer:


// 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 é definido em eip 1271,

    // como o valor de retorno para assinaturas válidas

    bytes4 internal constant MAGICVALUE = 0x1626ba7e;

    bytes4 internal constant INVALID_SIGNATURE = 0xffffffff;

    // lógica multisig básica, precisamos de 3 usos com a função DEFAULT_ADMIN_ROLE, para assinar uma mensagem e validá-la

    uint256 minimumSignatures = 3;

    mapping(bytes32 => uint256) public messageSignatures;

    constructor() {

        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);

    }

    //só uma função para adicionar uma assinatura

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

        address signer = _messageHash.recover(_signature);

        if (hasRole(DEFAULT_ADMIN_ROLE, signer)) {

            messageSignatures[_messageHash] += 1;

        }

    }

    // Isso vem do 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

Como a verificação da carteira geralmente funciona

Vejamos este diagrama, que explica como funciona um fluxo comum de autenticação web3:

  1. O servidor gera um nonce, um texto aleatório para o usuário assinar.

  2. O usuário assinou o nonce com a carteira.

  3. O servidor verifica a assinatura usando errcover para validar se foi assinada com a carteira certa.

  4. O servidor fornece um token ( geralmente JWT ) usado para interagir com o site e os serviços.

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

Isso é bastante simples e provavelmente é de conhecimento comum para a maioria dos desenvolvedores da web3, mas está incompleto.

É fácil entender por que as carteiras de contrato não passam por isso, pois não podem assinar, o back-end nunca verificará suas assinaturas.

Como podemos fazer a verificação da carteira funcionar para contratos inteligentes?

É bastante simples, olhando para a etapa ( 5 ) no diagrama acima, devemos fazer uma solicitação ao contrato e verificar se a assinatura é aprovada por ele.

Aqui está um exemplo de javascript usando ethers:

  1. Verificamos se o assinante é uma carteira ou um contrato.

  2. Para contratos, estamos verificando a assinatura chamando o contrato de ERC1271.

  3. Para o EOA, verificamos a assinatura normalmente.


import { providers, utils, Contract } from "ethers";

import IERC1271Abi from "./IERC1271Abi"; // 

const MAGICVALUE = 0x1626ba7e;

// verifica se a assinatura é 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 a mensagem ou a conta descentralizada (carteira do 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 a mensagem por uma conta própria externamente (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

E o lado da carteira das coisas?

Felizmente, temos uma solução unificada para autenticar carteiras, é gratuita, simples, de código aberto e suporta quase qualquer carteira pronta para uso, incluindo carteiras de contrato inteligentes, desde que seu site suporte a conexão com uma carteira, você está praticamente pronto deste lado.

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

Criar um cliente WalletConnect para um contrato inteligente é bastante simples, e abordarei isso em um post futuro, mas a maioria das carteiras de contrato tem seus próprios clientes, por exemplo, gnosis safe.

O que vem depois

Na verdade, escrevi este post porque queria usar a gnose safe, mas estou bastante decepcionado com o OpenSea

não suportando contratos EIP-1271 como carteiras, como você pode ver, é bem simples, e apoia mais descentralização e anonimato na blockchain, permitindo que equipes e DAOs colaborem com segurança em projetos da web3.

Gnosis tentou fazer isso acontecer em 2021 no Twitter. 8 meses atrás havia um Post no Reddit, na comunidade  OpenSea

onde eles deram essa resposta genérica a alguns usuários:

Atualmente, as carteiras Multisig não são uma opção oferecida na plataforma OpenSea. No entanto, estamos constantemente atualizando e adicionando recursos para melhorar a experiência do usuário. Nesse caso, enviarei esse feedback para nossa equipe de produtos.


Este artigo foi escrito por Liron Navon e traduzido por Adriano P. de Araujo. O original em inglês pode ser encontrado aqui.

Top comments (0)