WEB3DEV

Cover image for Por que você nunca deve aprovar contratos inteligentes proxy para gastos com tokens?
Fatima Lima
Fatima Lima

Posted on • Atualizado em

Por que você nunca deve aprovar contratos inteligentes proxy para gastos com tokens?

Se o título clickbait (caça-clique) o trouxe aqui, deixe-me começar com um comentário aliviador, você provavelmente interagiu com alguns contratos inteligentes proxy e continuará interagindo com eles sem notar. A maioria deles são seguros. Entretanto, os contratos inteligentes proxy podem ser facilmente usados para roubar dinheiro de sua carteira sem que você se dê conta. Vou tentar explicar como isso pode ser feito e como você pode se proteger.

Image description

Foto de Jason Pofahl em Unsplash


Sumário

1 . O que são contratos inteligentes proxy

2 . Como se proteger


O que são contratos inteligentes proxy

Os contratos inteligentes que são implantados na blockchain são imutáveis como a maioria das coisas relacionadas às blockchains. Isto significa que quando um contrato inteligente é implantado, seu código fonte não pode ser mudado e ele é gravado em pedra para sempre. Entretanto, existe o padrão proxy que torna seus contratos atualizáveis. Em resumo, os proxies são contratos inteligentes que são implantados separadamente e podem copiar o código (implementação) de outro contrato. O contrato que eles copiam pode ser atualizado com uma simples chamada de função sempre que o proprietário quiser. (Isto é conseguido com a função delegatecall da Solidity. Se você estiver interessado em saber mais sobre os proxies, você pode começar lendo [a documentação da OpenZeppelin]) (https://docs.openzeppelin.com/contracts/4.x/api/proxy)
Por exemplo, digamos que exista o contrato proxy A e ele copie o código do contrato B. Quando você quiser atualizar o seu contrato, você pode implantar um novo contrato C e fazer com que A copie sua implementação. Como os usuários só interagem com A em relação às transações, nada é alterado do seu lado.

O que são aprovações?

Quando falamos de uma moeda, isso geralmente significa um token ERC-20. O ERC-20 é um padrão e vem com algumas funções essenciais. Uma delas é a transfer(), e você usa essa função, por exemplo, quando você está transferindo $USDC para seus amigos. Há também outra função que é a transferFrom(), que permite que outros endereços transfiram seus fundos. Por exemplo, um contrato inteligente que permite cunhar um NFT por algum valor de $USDC chamará esta função durante a cunhagem. Entretanto, sem qualquer proteção, todos poderão transferir os fundos de outras pessoas, é aqui que entram em jogo as aprovações.
No exemplo da cunhagem do NFT acima, se o preço da cunhagem for 100 $USDC, você precisa primeiro aprovar o contrato do NFT para gastar pelo menos 100 $USDC de sua própria conta. Somente depois de chamar a função de aprovação (que também faz parte do padrão ERC-20), sua chamada para a função de cunhagem não falhará.
Para facilitar o uso e não incomodar os usuários com aprovações o tempo todo, os DAPPs normalmente fazem com que você aprove o gasto de 1,1579209e+77 tokens (o número máximo possível para um número inteiro não assinado de 256 bits). Isto significa que você basicamente dá a eles acesso a todos os seus tokens. Isto não é um problema com protocolos respeitáveis, pois seu contrato inteligente é de código aberto e você pode checar as funções deles para ver se eles podem abusar desta aprovação.

Como aproveitar as aprovações com proxies?

Vamos assumir que um explorador primeiro crie um simples contrato inteligente bancário que lhe permite depositar alguns tokens nele e retirar seu saldo. O contrato inteligente parecerá algo semelhante ao contrato abaixo:

// contracts/Version1-Safe.sol
pragma solidity ^0.8.0;

interface IERC20 {
    function transferFrom(address from, address to, uint256 value) external returns(bool);

    function transfer(address to, uint256 value) external returns(bool);
}

contract Bank {
    mapping(address => uint256) public depositAmount;

    function deposit(uint256 amount) public {
        IERC20(0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56).transferFrom(msg.sender, address(this), amount);
        depositAmount[msg.sender] += amount;
    }

    function withdraw() public {
        IERC20(0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56).transfer(msg.sender, depositAmount[msg.sender]);
        depositAmount[msg.sender] = 0;
    }
}

Enter fullscreen mode Exit fullscreen mode

O explorador então cria um contrato inteligente proxy e copia a implementação deste código e desenvolve um website que lhe permite interagir com o proxy. (0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56 é o endereço do contrato de $BUSD na Smart Chain Binance, se você estiver se perguntando.)

Então você quer usar o contrato e, através de seu front-end, você o aprova para gastos de $BUSD, mas não verifica o valor da aprovação.

O explorador, mais tarde, pode atualizar o proxy para implementar uma versão maliciosa do contrato inteligente acima, que pode se parecer com o exemplo abaixo:

// contracts/Version1-Safe.sol
pragma solidity ^0.8.0;

interface IERC20 {
    function transferFrom(address from, address to, uint256 value) external returns(bool);

    function transfer(address to, uint256 value) external returns(bool);
}

contract Bank2 {
    mapping(address => uint256) public depositAmount;

    function deposit(uint256 amount) public {
        IERC20(0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56).transferFrom(msg.sender, address(this), amount);
        depositAmount[msg.sender] += amount;
    }

    function withdraw() public {
        IERC20(0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56).transfer(msg.sender, depositAmount[msg.sender]);
        depositAmount[msg.sender] = 0;
    }

    function steal(address from, address to, uint256 amount) public {
        IERC20(0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56).transferFrom(from, to, amount);
    }
}

Enter fullscreen mode Exit fullscreen mode

Após a atualização do proxy, a função steal() pode ser usada para transferir os fundos das pessoas que aprovaram o contrato em primeiro lugar.

Por exemplo, se você tinha 100 $BUSD em sua carteira e deu uma quantia "infinita" de aprovações e apenas depositou 50 $BUSD. O explorador pode transferir os outros 50 $BUSD diretamente para sua carteira.

Como se proteger

Em teoria, você nunca pode dar mais aprovações do que a quantia que você usará antes de cada transação. Mas, na prática , isto tornará sua vida realmente difícil, pois você precisaria, por exemplo, passar por duas transações, em vez de uma, cada vez que fosse usar o Uniswap.

Uma solução mais prática seria apenas dar infinitas aprovações a contratos inteligentes de boa reputação que não sejam proxies. Você pode verificar se um contrato inteligente é proxy no Etherscan, ou em seus forks para outras cadeias, com muita facilidade. E se você vir que o contrato é um proxy, é melhor não usá-lo de forma alguma ou ter cuidado com o valor que você está aprovando.

Mantenha-se em segurança!

Esse artigo foi escrito por Doğu Deniz Uğur e traduzido por Fátima Lima. O original pode ser lido aqui.

Top comments (0)